いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

.NET クラスライブラリ探訪-010 (System.Type)(03)(ジェネリックスに関連するメソッド・プロパティ)


System.Typeクラスのメソッドに関するサンプルです。


ジェネリック型を動的に生成する際に、重要なプロパティは以下です。

  • IsGenericType --> この型がジェネリック型かどうかを返します。
  • IsGenericDefinition --> このTypeオブジェクトが他のジェネリック型を構築できるかどうかを返します。
  • ContainsGenericParameters --> このTypeオブジェクトが現在型引数情報を指定されているかどうかを返します。


ジェネリック型を生成するには、以下のステップを踏みます。

  1. 型引数を指定しない状態でTypeオブジェクト生成
  2. MakeGenericTypeメソッドを使用して、型引数情報を付加。
  3. インスタンス


以下、サンプルです。

using System;
using System.Collections.Generic;
using System.Text;

namespace Gsf.Samples.ClassLibraryResearch.System {

    internal class SampleGenericModel<T> {

        T _value;

        public SampleGenericModel(T val) {
            _value = val;
        }

        public void PrintValueType() {
            Console.WriteLine("与えられた型引数は {0}", _value.GetType().FullName);
        }
    }

    /// <summary>
    /// System.Typeクラスのサンプルです。
    /// </summary>
    /// <remarks>
    /// System.Typeクラスのメソッドに関するサンプルです。
    /// 
    /// 主に、ジェネリック型関連のメソッドについて記述します。
    /// ジェネリック型を動的に生成する際に、重要なプロパティは以下です。
    /// ■IsGenericType             --> この型がジェネリック型かどうかを返します。
    /// ■IsGenericDefinition       --> このTypeオブジェクトが他のジェネリック型を構築できるかどうかを返します。
    /// ■ContainsGenericParameters --> このTypeオブジェクトが現在型引数情報を指定されているかどうかを返します。
    /// 
    /// ジェネリック型を生成するには、以下のステップを踏みます。
    /// 1.型引数を指定しない状態でTypeオブジェクト生成
    /// 2.MakeGenericTypeメソッドを使用して、型引数情報を付加。
    /// 3.インスタンス化
    /// </remarks>
    public class TypeResearcher3 : IExecutable {

        #region IExecutable メンバ

        public void Execute() {
            //
            // 型を明示的に指定して(typeof)、ジェネリック型を作成する方法。
            //
            // まず、型引数無しでTypeオブジェクトを構築します。
            //
            // このとき、生成されるTypeオブジェクトには、以下の特徴があります。
            // ■IsGenericTypeがtrue
            // ■IsGenericDefinitionがtrue
            // IsGenericDefinitionがtrueである場合のみ、後から型引数情報を指定して,MakeGenericTypeメソッドを
            // 呼ぶことが出来ます。
            //
            Type genericType = typeof(SampleGenericModel<>);
            PrintTypeInfo(genericType);

            //
            // 次に、上で作成したTypeオブジェクトに型情報を与えて、新たなTypeオブジェクトを作成します。
            //
            // 型引数情報を付加すると、Typeのプロパティ情報が以下のようになります。
            // ■IsGenericTypeがtrue
            // ■IsGenericDefinitionがfalse
            // ■ContainsGenericParametersがfalse
            //
            Type constructGenericType1 = genericType.MakeGenericType(new Type[] { typeof(string) });
            Type constructGenericType2 = genericType.MakeGenericType(new Type[] { typeof(int) });
            PrintTypeInfo(constructGenericType1);
            PrintTypeInfo(constructGenericType2);

            //
            // 上記で作成したTypeオブジェクトからインスタンスを生成できるかどうかを確認してみます。
            //
            try {
                //
                // ERROR:この呼び出しは失敗します。(型引数情報が与えられていないため)
                //
                Activator.CreateInstance(genericType, new object[] { "gsf_zero1" });
            } catch {
            }

            object obj = Activator.CreateInstance(constructGenericType1, new object[] { "gsf_zero1" });
            (obj as SampleGenericModel<string>).PrintValueType();

            obj = Activator.CreateInstance(constructGenericType2, new object[] { 100 });
            (obj as SampleGenericModel<int>).PrintValueType();

            Console.WriteLine("");

            //
            // 型を文字列で指定して、そこからジェネリック型を作成する方法。
            // IsGenericDefinitionをtrueにした状態のTypeオブジェクトを生成する場合は、
            //      XXXGenericClass`1[T]
            // のように指定するのではなく、
            //      XXXGenericClass`1
            // のように、型引数の個数までを記述してTypeオブジェクトを生成します。
            //
            //
            // genericType = Type.GetType("Gsf.Samples.ClassLibraryResearch.System.SampleGenericModel`1[T]");
            // の指定の仕方だと、nullが帰ります。
            // 上のように、フルで型引数まで指定して一気に生成する場合は、型引数もちゃんとSystem.Stringのように
            // 指定する必要があります。
            //
            genericType = Type.GetType("Gsf.Samples.ClassLibraryResearch.System.SampleGenericModel`1");
            PrintTypeInfo(genericType);

            constructGenericType1 = genericType.MakeGenericType(new Type[] { Type.GetType("System.String") });
            constructGenericType2 = genericType.MakeGenericType(new Type[] { Type.GetType("System.Int32") });
            PrintTypeInfo(constructGenericType1);
            PrintTypeInfo(constructGenericType2);

            //
            // 上記で作成したTypeオブジェクトからインスタンスを生成できるかどうかを確認してみます。
            //
            try {
                //
                // ERROR:この呼び出しは失敗します。(型引数情報が与えられていないため)
                //
                Activator.CreateInstance(genericType, new object[] { "gsf_zero1" });
            } catch {
            }

            obj = Activator.CreateInstance(constructGenericType1, new object[] { "gsf_zero1" });
            (obj as SampleGenericModel<string>).PrintValueType();

            obj = Activator.CreateInstance(constructGenericType2, new object[] { 100 });
            (obj as SampleGenericModel<int>).PrintValueType();
        }

        #endregion

        public void PrintTypeInfo(Type aType) {
            Console.WriteLine(aType);
            Console.WriteLine("IsGenericType={0}",             aType.IsGenericType);
            Console.WriteLine("IsGenericTypeDefinition={0}",   aType.IsGenericTypeDefinition);
            Console.WriteLine("ContainsGenericParameters={0}", aType.ContainsGenericParameters);
            Console.WriteLine("");
        }
    }

    public class EntryPoint {
        
        static void Main(){
            new TypeResearcher3().Execute();
            Console.ReadLine();
        }
    }
}


================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。