えんじにあ雑記!

開発していて学んだことをまとめていきます!

C#でGolangのdeferっぽいこと

f:id:flat-M_M:20200407230440j:plain

最近Golangでサーバサイドを書く機会が増えてきていて、Golangの言語の特徴になるほど〜〜、いいねって感じることがあるのですが、

その中でもdeferが個人的にかなり便利!だったのでC#でもできないかと思いなんちゃってdeferを実装してみた件

はじめに

普段Unityで開発していて、たまにバックエンドをGoで書いたりするのですが、ある日ふと「C#でdefer使いたい」って思ったので、実装してみました。

下記のサイトを参考にさせて頂きました。

C#でDeferする

IDisposableインタフェースについて

また、こちらの記事でも述べられているように、C#のバージョンアップに伴いdeferの採用が議論されているようです。 

実装コード

using System;
using System.Collections.Generic;
using System.Extension;

namespace DeferSample
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var defer = new Defer ()) {
                defer.Register (() => { Console.Write ("Function!"); });
                defer.Register (() => { Console.Write ("defer "); });
                defer.Register(() => { Console.Write ("Hello, "); });
                Console.WriteLine ("Hello World!");
            }
        }
    }
}

namespace System.Extension
{
    public class Defer : IDisposable
    {
        private Stack<Action> actions = new Stack<Action> ();

        private bool disposed = false;

        public void Register(Action action)
        {
            actions.Push (action);
        }

        private void ExecuteAll()
        {
            foreach (var action in actions) {
                action ();
            }
        }

        public void Dispose ()
        {
            ExecuteAll ();
            Dispose (true);
            GC.SuppressFinalize (this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed) {
                if (disposing) {
                    actions.GetEnumerator ().Dispose ();
                }
                this.disposed = true;
            }
        }

        ~Defer()
        {
            Dispose (false);
        }
    }
}

解説①関数の管理

今回はさくっと実装できるか試したかっただけなので、deferで呼び出せるのは引数なしの関数だけにしました。 deferで宣言した関数はスタックで管理することでいわゆるLIFOで管理することができるようにしています。

解説②IDisposableの実装

usingを使って、明示的に書けた方がわかりやすいだろうということで、IDisposableを実装してみました。 公式ドキュメントのサンプルを読みつつ実装したのですが、ポイントかなと思うのは、自分からDisposeする場合と、外部からDisposeされる場合の2パターンがあるということです。

実行結果

Hello World!
Hello, Deffer Function!

正しく遅延実行されていますね👍

最後に

使いたいと思ってさくっと作ってみたので、パフォーマンスなどは意識できていませんが、Golangのdeferのようなことができるのはタイミングによってはありがたいかなと思います。( ̄▽ ̄)