SourceChord

C#とXAML好きなプログラマの備忘録。最近はWPF系の話題が中心です。

引数に渡したインスタンスの実際の型に応じて呼び出すメソッドを切り替える

C#のdynamicにこんな使い方あるのか!?と、、ちょっと目からウロコだったので試してみました。


下記ページの、dynamicを使ったマルチディスパッチの方法を参考にしてます。
[雑記] 多重ディスパッチ (C# によるプログラミング入門)

サンプル

あるクラスの派生型のインスタンスを基底クラスの型で保持しておき、その変数の実際の型に応じてメソッドを呼び分けたいとします。
以下のに書くと、常にBaseクラスを引数にとるメソッドオーバーロードが呼び出され、インスタンスの実際の型に応じた呼び分けはできません。

    class Base
    {
    }

    class DerivedA : Base
    {
    }

    class DerivedB : Base
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            Base obj = new DerivedA();

            Invoke(obj);
        }


        static void Invoke(Base obj)
        {
            Console.WriteLine("Invoke(Base obj)");
        }
        static void Invoke(DerivedA obj)
        {
            Console.WriteLine("Invoke(DerivedA obj)");
        }
        static void Invoke(DerivedB obj)
        {
            Console.WriteLine("Invoke(DerivedB obj)");
        }
    }
補足

ここにサンプルで書いたような1変数のディスパッチであれば、
このサンプルのInvokeメソッドをベースクラス側に持っていって、仮想関数を用いたポリモーフィズムにすればよいのです。

でも、どうしてもこういう風にメソッド呼び出し側が、インスタンスの実際の型に応じて呼び分けたい、ということもあるかもしれません。

dynamicを用いてディスパッチ

ここで、メソッド呼び出し時に引数をdynamic型にしてから呼び出してみます。
以下のようにするだけです。

Invoke(obj as dynamic);

これで、Invokeメソッドの引数に渡したobjの型に応じたメソッドが呼び出されるようになります。
引数の実行時の型に応じた呼び出し先切り替えがしたい時には、
dynamicを使った方法も、お手軽な手段として覚えておくと、どこかで使えるかもしれません。

この方法での注意点

で、この方法を使った時に注意すべき点です。
もしBaseから派生する別のDerivedCクラスなどを作ったときには、
Invoke(DerivedC obj)という感じで、追加した型に応じたメソッドオーバーロードを用意する必要があります。

メソッドを追加し忘れると、コンパイルは通るけど実際に追加したDerivedC型のインスタンスが渡されたときに、ディスパッチできずに、実行時エラーとなってしまいます。