[C# 入門] 動的型付け変数(dynamic型)について

C#で変数を使う場合、あらかじめデータ型を指定する必要がありますが、dynamic型の変数はプログラム実行時に型がチェックされます。

データ型を指定している(dynamic以外)場合、その変数(の中に入っているオブジェクト)がどんなメンバーを持っているのか判明しているので、持っていないメンバー名をコードに書くとコンパイルエラーになります。

dynamic型の場合、コードを書いているときにどんなメンバーを持ったオブジェクトかというチェックをしないためコンパイルエラーになりません。
その代わり、プログラム実行時に持っていないメンバーにアクセスしようとしたり想定外の使い方をするとエラーになります。

実行時に型をチェックするので動的な型と呼ばれています。それ以外のコードを書いている時に型が決まっているものを静的な型といいます。

スポンサーリンク

dynamic型の使い方

dynamic型の変数は他の変数と同じようにdynamicを付けて宣言します。

using System;
class Program
{
    public static void Main()
    {
        dynamic dy1 = 10;
        dynamic dy2 = new int[] { 1, 2, 3 };
        dynamic dy3 = "abc";

        Console.WriteLine(dy1 + 20);
        Console.WriteLine(dy2[1]);
        Console.WriteLine(dy3.Length);
    }
}
30
2
3

型推論varを使った場合、右辺に指定された型の変数になりますが、dynamicで宣言した変数は10を代入しようがint[]を代入しようがdynamic型になります。

dynamic型の変数はどんなメンバーを持っているオブジェクトかというチェックをしないため、VisualStudioでコードを書いているときに入力候補(インテリセンス)がでてきません。スペルミスなどでメンバーが見つからないと実行時にエラーになるので注意が必要です。

dynamic型は中に入っている値の型と同じ型の変数に変換(代入)することができます。

using System;
class Program
{
    public static void Main()
    {
        dynamic dy1 = 10;
        dynamic dy2 = new int[] { 1, 2, 3 };
        dynamic dy3 = "abc";

        // それぞれの型に変換
        int i = dy1;
        int[] ary = dy2;
        string s = dy3;

        Console.WriteLine(i + 20);
        Console.WriteLine(ary[1]);
        Console.WriteLine(s.Length);
    }
}

実行時に型がチェックされるので、違う型のデータを途中で入れることもできます。

using System;
class Program
{
    public static void Main()
    {
        // dynamic型変数の宣言
        dynamic dy;

        // int型の値を入れる
        dy = 10;
        Console.WriteLine($"dy = {dy}");

        // datetime型の値を入れる
        dy = DateTime.Now;
        Console.WriteLine($"dy = {dy}");
    }
}
dy = 10
dy = 2020/06/07 13:14:07
スポンサーリンク

どんなときにdynamic型を使うのか

動的言語ランタイムとの連携

動的言語ランタイム(DLR)という、C#から動的言語(スクリプト言語)を呼び出す仕組みがあります。スクリプト言語は実行時に読み込まれるまでどんなメンバーを持つオブジェクトがあるのかわかりません、そのためdynamic型を使って実行時に型をチェックします。

呼び出せるスクリプト言語にはPython(IronPython)やらRuby(IronRuby)があるみたいです。そのうちくわしくまとめたいと思います。

object型の変数に入った匿名クラスのメンバーにアクセスする

object型の変数にstring型などの値が入っている場合、string型としてその変数を使うにはキャストして型を変換する必要があります。

object o = "abc";

// string.Lengthを使いたい場合、string型にキャストする必要がある
string s = (string)o;
Console.WriteLine(s.Length);

object型の変数に匿名クラスのオブジェクトが入っている場合など、どんな型にキャストすればいいかわからないときにdynamic型を使ってメンバーにアクセスすることができます。

using System;
class Program
{
    public static void Main()
    {
        object o = new { Name = "abc", ID = 100 };

        // どんな型にキャストするかわからないときdynamic型にキャストしてメンバーにアクセス
        dynamic dy = o as dynamic;
        Console.WriteLine($"Name = {dy.Name}, ID = {dy.ID}");
    }
}
Name = abc, ID = 100

遅延バインド

DLLやCOMにあるクラスやメソッドを最初から読み込む(参照設定しておく)のではなく、プログラム実行時に読み込むことを遅延バインドといいます。

遅延バインドはDLL、COMを読み込むまでどんなクラス、メソッドがあるかわからないためdynamic型を使いメンバーにアクセスします。

こんなメソッドを持つDLLファイル(SampleLib.dll)が実行ファイルと同じ場所にあったとして、

using System;
namespace SampleLib
{
    public class LibClass
    {
        public int TestMethod(int param1) => param1 + 100;
    }
}

こんな感じにDLLファイルを読み込んで使うことができます。

using System;
using System.Reflection;
class Program
{
    public static void Main()
    {
        // SampleLib.dllファイルを読み込む
        var lib = Assembly.LoadFrom("SampleLib.dll");

        // クラスの情報を取得
        var type = lib.GetType("SampleLib.LibClass");

        // クラスのインスタンスを作成
        // 実行して読み込むまで型がわからないのでdynamic型変数に入れる
        dynamic cls = Activator.CreateInstance(type);

        // メソッドを呼び出す
        int result = cls.TestMethod(10);

        // 結果の表示
        Console.WriteLine(result);
    }
}
110

遅延バインドについては、そのうちリフレクションの記事を書こうと思います。

同じ名前のメンバーを持つオブジェクトを紐づける

「同じ名前のメンバーを持つオブジェクトであればクラスは何でもいいや」という書き方(ダックタイピングというらしい)ができます。

こんな感じです。

using System;
class Program
{
    public static void Main()
    {
        var no1 = new TestClass_No1();
        Call_Method(no1);

        var no2 = new TestClass_No2();
        Call_Method(no2);
    }

    static void Call_Method(dynamic dy)
    {
        // dynamic型変数に入っているであろうMethodという名前のメソッドを呼び出す
        dy.Method();
    }
}

class TestClass_No1
{
    public void Method() => Console.WriteLine("TestClass_No1のMethodを実行");
}

class TestClass_No2
{
    public void Method() => Console.WriteLine("TestClass_No2のMethodを実行");
}
TestClass_No1のMethodを実行
TestClass_No2のMethodを実行

TestClass_No1 と TestClass_No2 は同じ名前のメソッドを持っているだけで、つながり(同じインターフェースを実装している、同じクラスを継承している)はないが、dynamic型を使ってゆるーく紐づけることができます。

スポンサーリンク

動的なオブジェクト

名前空間System.Dynamic内にはDynamicObjectクラス、ExpandoObjectクラスという動的にメンバーを追加できるクラスがあります。
このクラスを使うとクラスを定義することなくオブジェクトのメンバーを動的に作成することができます。

詳しい使い方は別記事で紹介しています。


C# プログラミング講座に戻る

コメント

タイトルとURLをコピーしました