次の方法で共有


型を動的に読み込み使用する

リフレクションは、暗黙的な遅延バインディングを実装するために言語コンパイラによって使用されるインフラストラクチャを提供します。 バインディングは、一意に指定された型に対応する宣言 (つまり、実装) を検索するプロセスです。 このプロセスがコンパイル時ではなく実行時に発生すると、遅延バインディングと呼ばれます。 Visual Basic では、コードで暗黙的な遅延バインディングを使用できます。Visual Basic コンパイラは、リフレクションを使用してオブジェクト型を取得するヘルパー メソッドを呼び出します。 ヘルパー メソッドに渡される引数により、実行時に適切なメソッドが呼び出されます。 これらの引数は、メソッドを呼び出すインスタンス (オブジェクト)、呼び出されたメソッドの名前 (文字列)、および呼び出されたメソッドに渡される引数 (オブジェクトの配列) です。

次の例では、Visual Basic コンパイラはリフレクションを暗黙的に使用して、コンパイル時に型が不明なオブジェクトのメソッドを呼び出します。 HelloWorld クラスには、PrintHello メソッドに渡されるテキストと連結された "Hello World" を出力するPrintHello メソッドがあります。 この例で呼び出されるPrintHello メソッドは実際にはType.InvokeMemberです。Visual Basic コードでは、実行時 (遅延バインディング) ではなく、コンパイル時 (早期バインディング) にオブジェクト (PrintHello) の型がわかっていたかのようにhelloObj メソッドを呼び出すことができます。

Module Hello
    Sub Main()
        ' Sets up the variable.
        Dim helloObj As Object
        ' Creates the object.
        helloObj = new HelloWorld()
        ' Invokes the print method as if it was early bound
        ' even though it is really late bound.
        helloObj.PrintHello("Visual Basic Late Bound")
    End Sub
End Module

カスタム結合

遅延バインディングのためにコンパイラによって暗黙的に使用されるだけでなく、リフレクションをコードで明示的に使用して遅延バインディングを実行できます。

共通言語ランタイムは複数のプログラミング言語をサポートしており、これらの言語のバインド規則は異なります。 早期バインドの場合、コード ジェネレーターはこのバインディングを完全に制御できます。 ただし、リフレクションによる遅延バインディングでは、バインディングはカスタマイズされたバインディングによって制御される必要があります。 Binder クラスは、メンバーの選択と呼び出しのカスタム 制御を提供します。

カスタム バインドを使用すると、実行時にアセンブリを読み込み、そのアセンブリ内の型に関する情報を取得し、必要な型を指定してから、その型のメソッドまたはアクセス フィールドまたはプロパティを呼び出すことができます。 この手法は、オブジェクトの型がユーザー入力に依存している場合など、コンパイル時にオブジェクトの型がわからない場合に便利です。

次の例は、引数の型変換を提供しない単純なカスタム バインダーを示しています。 Simple_Type.dllのコードは、メインの例の前にあります。 必ず Simple_Type.dll ビルドしてから、ビルド時にプロジェクトに参照を含めます。

// Code for building SimpleType.dll.
using System;
using System.Reflection;
using System.Globalization;
using Simple_Type;

namespace Simple_Type
{
    public class MySimpleClass
    {
        public void MyMethod(string str, int i)
        {
            Console.WriteLine("MyMethod parameters: {0}, {1}", str, i);
        }

        public void MyMethod(string str, int i, int j)
        {
            Console.WriteLine("MyMethod parameters: {0}, {1}, {2}",
                str, i, j);
        }
    }
}

namespace Custom_Binder
{
    class MyMainClass
    {
        static void Main()
        {
            // Get the type of MySimpleClass.
            Type myType = typeof(MySimpleClass);

            // Get an instance of MySimpleClass.
            MySimpleClass myInstance = new MySimpleClass();
            MyCustomBinder myCustomBinder = new MyCustomBinder();

            // Get the method information for the particular overload
            // being sought.
            MethodInfo myMethod = myType.GetMethod("MyMethod",
                BindingFlags.Public | BindingFlags.Instance,
                myCustomBinder, new Type[] {typeof(string),
                typeof(int)}, null);
            Console.WriteLine(myMethod.ToString());

            // Invoke the overload.
            myType.InvokeMember("MyMethod", BindingFlags.InvokeMethod,
                myCustomBinder, myInstance,
                new Object[] {"Testing...", (int)32});
        }
    }

    // ****************************************************
    //  A simple custom binder that provides no
    //  argument type conversion.
    // ****************************************************
    class MyCustomBinder : Binder
    {
        public override MethodBase BindToMethod(
            BindingFlags bindingAttr,
            MethodBase[] match,
            ref object[] args,
            ParameterModifier[] modifiers,
            CultureInfo culture,
            string[] names,
            out object state)
        {
            if (match == null)
            {
                throw new ArgumentNullException("match");
            }
            // Arguments are not being reordered.
            state = null;
            // Find a parameter match and return the first method with
            // parameters that match the request.
            foreach (MethodBase mb in match)
            {
                ParameterInfo[] parameters = mb.GetParameters();

                if (ParametersMatch(parameters, args))
                {
                    return mb;
                }
            }
            return null;
        }

        public override FieldInfo BindToField(BindingFlags bindingAttr,
            FieldInfo[] match, object value, CultureInfo culture)
        {
            if (match == null)
            {
                throw new ArgumentNullException("match");
            }
            foreach (FieldInfo fi in match)
            {
                if (fi.GetType() == value.GetType())
                {
                    return fi;
                }
            }
            return null;
        }

        public override MethodBase SelectMethod(
            BindingFlags bindingAttr,
            MethodBase[] match,
            Type[] types,
            ParameterModifier[] modifiers)
        {
            if (match == null)
            {
                throw new ArgumentNullException("match");
            }

            // Find a parameter match and return the first method with
            // parameters that match the request.
            foreach (MethodBase mb in match)
            {
                ParameterInfo[] parameters = mb.GetParameters();
                if (ParametersMatch(parameters, types))
                {
                    return mb;
                }
            }

            return null;
        }

        public override PropertyInfo SelectProperty(
            BindingFlags bindingAttr,
            PropertyInfo[] match,
            Type returnType,
            Type[] indexes,
            ParameterModifier[] modifiers)
        {
            if (match == null)
            {
                throw new ArgumentNullException("match");
            }
            foreach (PropertyInfo pi in match)
            {
                if (pi.GetType() == returnType &&
                    ParametersMatch(pi.GetIndexParameters(), indexes))
                {
                    return pi;
                }
            }
            return null;
        }

        public override object ChangeType(
            object value,
            Type myChangeType,
            CultureInfo culture)
        {
            try
            {
                object newType;
                newType = Convert.ChangeType(value, myChangeType);
                return newType;
            }
            // Throw an InvalidCastException if the conversion cannot
            // be done by the Convert.ChangeType method.
            catch (InvalidCastException)
            {
                return null;
            }
        }

        public override void ReorderArgumentArray(ref object[] args,
            object state)
        {
            // No operation is needed here because BindToMethod does not
            // reorder the args array. The most common implementation
            // of this method is shown below.

            // ((BinderState)state).args.CopyTo(args, 0);
        }

        // Returns true only if the type of each object in a matches
        // the type of each corresponding object in b.
        private bool ParametersMatch(ParameterInfo[] a, object[] b)
        {
            if (a.Length != b.Length)
            {
                return false;
            }
            for (int i = 0; i < a.Length; i++)
            {
                if (a[i].ParameterType != b[i].GetType())
                {
                    return false;
                }
            }
            return true;
        }

        // Returns true only if the type of each object in a matches
        // the type of each corresponding entry in b.
        private bool ParametersMatch(ParameterInfo[] a, Type[] b)
        {
            if (a.Length != b.Length)
            {
                return false;
            }
            for (int i = 0; i < a.Length; i++)
            {
                if (a[i].ParameterType != b[i])
                {
                    return false;
                }
            }
            return true;
        }
    }
}
' Code for building SimpleType.dll.
Imports System.Reflection
Imports System.Globalization
Imports Simple_Type

Namespace Simple_Type
    Public Class MySimpleClass
        Public Sub MyMethod(str As String, i As Integer)
            Console.WriteLine("MyMethod parameters: {0}, {1}", str, i)
        End Sub

        Public Sub MyMethod(str As String, i As Integer, j As Integer)
            Console.WriteLine("MyMethod parameters: {0}, {1}, {2}",
                str, i, j)
        End Sub
    End Class
End Namespace

Namespace Custom_Binder
    Class MyMainClass
        Shared Sub Main()
            ' Get the type of MySimpleClass.
            Dim myType As Type = GetType(MySimpleClass)

            ' Get an instance of MySimpleClass.
            Dim myInstance As New MySimpleClass()
            Dim myCustomBinder As New MyCustomBinder()

            ' Get the method information for the particular overload
            ' being sought.
            Dim myMethod As MethodInfo = myType.GetMethod("MyMethod",
                BindingFlags.Public Or BindingFlags.Instance,
                myCustomBinder, New Type() {GetType(String),
                GetType(Integer)}, Nothing)
            Console.WriteLine(myMethod.ToString())

            ' Invoke the overload.
            myType.InvokeMember("MyMethod", BindingFlags.InvokeMethod,
                myCustomBinder, myInstance,
                New Object() {"Testing...", CInt(32)})
        End Sub
    End Class

    ' ****************************************************
    '  A simple custom binder that provides no
    '  argument type conversion.
    ' ****************************************************
    Class MyCustomBinder
        Inherits Binder

        Public Overrides Function BindToMethod(bindingAttr As BindingFlags,
            match() As MethodBase, ByRef args As Object(),
            modIfiers() As ParameterModIfier, culture As CultureInfo,
            names() As String, ByRef state As Object) As MethodBase

            If match is Nothing Then
                Throw New ArgumentNullException("match")
            End If
            ' Arguments are not being reordered.
            state = Nothing
            ' Find a parameter match and return the first method with
            ' parameters that match the request.
            For Each mb As MethodBase in match
                Dim parameters() As ParameterInfo = mb.GetParameters()

                If ParametersMatch(parameters, args) Then
                    Return mb
                End If
            Next mb
            Return Nothing
        End Function

        Public Overrides Function BindToField(bindingAttr As BindingFlags,
            match() As FieldInfo, value As Object, culture As CultureInfo) As FieldInfo
            If match Is Nothing
                Throw New ArgumentNullException("match")
            End If
            For Each fi As FieldInfo in match
                If fi.GetType() = value.GetType() Then
                    Return fi
                End If
            Next fi
            Return Nothing
        End Function

        Public Overrides Function SelectMethod(bindingAttr As BindingFlags,
            match() As MethodBase, types() As Type,
            modifiers() As ParameterModifier) As MethodBase

            If match Is Nothing Then
                Throw New ArgumentNullException("match")
            End If

            ' Find a parameter match and return the first method with
            ' parameters that match the request.
            For Each mb As MethodBase In match
                Dim parameters() As ParameterInfo = mb.GetParameters()
                If ParametersMatch(parameters, types) Then
                    Return mb
                End If
            Next mb

            Return Nothing
        End Function

        Public Overrides Function SelectProperty(
            bindingAttr As BindingFlags, match() As PropertyInfo,
            returnType As Type, indexes() As Type,
            modIfiers() As ParameterModIfier) As PropertyInfo

            If match Is Nothing Then
                Throw New ArgumentNullException("match")
            End If
            For Each pi As PropertyInfo In match
                If pi.GetType() = returnType And
                    ParametersMatch(pi.GetIndexParameters(), indexes) Then
                    Return pi
                End If
            Next pi
            Return Nothing
        End Function

        Public Overrides Function ChangeType(
            value As Object,
            myChangeType As Type,
            culture As CultureInfo) As Object

            Try
                Dim newType As Object
                newType = Convert.ChangeType(value, myChangeType)
                Return newType
                ' Throw an InvalidCastException If the conversion cannot
                ' be done by the Convert.ChangeType method.
            Catch
                Return Nothing
            End Try
        End Function

        Public Overrides Sub ReorderArgumentArray(ByRef args() As Object, state As Object)
            ' No operation is needed here because BindToMethod does not
            ' reorder the args array. The most common implementation
            ' of this method is shown below.

            ' ((BinderState)state).args.CopyTo(args, 0)
        End Sub

        ' Returns true only If the type of each object in a matches
        ' the type of each corresponding object in b.
        Private Overloads Function ParametersMatch(a() As ParameterInfo, b() As Object) As Boolean
            If a.Length <> b.Length Then
                Return false
            End If
            For i As Integer = 0 To a.Length - 1
                If a(i).ParameterType <> b(i).GetType() Then
                    Return false
                End If
            Next i
            Return true
        End Function

        ' Returns true only If the type of each object in a matches
        ' the type of each corresponding enTry in b.
        Private Overloads Function ParametersMatch(a() As ParameterInfo,
            b() As Type) As Boolean

            If a.Length <> b.Length Then
                Return false
            End If
            For i As Integer = 0 To a.Length - 1
                If a(i).ParameterType <> b(i)
                    Return false
                End If
            Next
            Return true
        End Function
    End Class
End Namespace

InvokeMember と CreateInstance

Type.InvokeMemberを使用して、型のメンバーを呼び出します。 CreateInstanceActivator.CreateInstanceなど、さまざまなクラスのAssembly.CreateInstanceメソッドは、指定された型の新しいインスタンスを作成する特殊な形式のInvokeMemberです。 Binder クラスは、これらのメソッドのオーバーロードの解決と引数の強制化に使用されます。

次の例は、引数強制型変換 (型変換) とメンバー選択の 3 つの可能な組み合わせを示しています。 ケース 1 では、引数強制またはメンバーの選択は必要ありません。 ケース 2 では、メンバーの選択のみが必要です。 ケース 3 では、引数強制型変換のみが必要です。

public class CustomBinderDriver
{
    public static void Main()
    {
        Type t = typeof(CustomBinderDriver);
        CustomBinder binder = new CustomBinder();
        BindingFlags flags = BindingFlags.InvokeMethod | BindingFlags.Instance |
            BindingFlags.Public | BindingFlags.Static;
        object[] args;

        // Case 1. Neither argument coercion nor member selection is needed.
        args = new object[] {};
        t.InvokeMember("PrintBob", flags, binder, null, args);

        // Case 2. Only member selection is needed.
        args = new object[] {42};
        t.InvokeMember("PrintValue", flags, binder, null, args);

        // Case 3. Only argument coercion is needed.
        args = new object[] {"5.5"};
        t.InvokeMember("PrintNumber", flags, binder, null, args);
    }

    public static void PrintBob()
    {
        Console.WriteLine("PrintBob");
    }

    public static void PrintValue(long value)
    {
        Console.WriteLine($"PrintValue({value})");
    }

    public static void PrintValue(string value)
    {
        Console.WriteLine("PrintValue\"{0}\")", value);
    }

    public static void PrintNumber(double value)
    {
        Console.WriteLine($"PrintNumber ({value})");
    }
}
Public Class CustomBinderDriver
    Public Shared Sub Main()
        Dim t As Type = GetType(CustomBinderDriver)
        Dim binder As New CustomBinder()
        Dim flags As BindingFlags = BindingFlags.InvokeMethod Or BindingFlags.Instance Or
            BindingFlags.Public Or BindingFlags.Static
        Dim args() As Object

        ' Case 1. Neither argument coercion nor member selection is needed.
        args = New object() {}
        t.InvokeMember("PrintBob", flags, binder, Nothing, args)

        ' Case 2. Only member selection is needed.
        args = New object() {42}
        t.InvokeMember("PrintValue", flags, binder, Nothing, args)

        ' Case 3. Only argument coercion is needed.
        args = New object() {"5.5"}
        t.InvokeMember("PrintNumber", flags, binder, Nothing, args)
    End Sub

    Public Shared Sub PrintBob()
        Console.WriteLine("PrintBob")
    End Sub

    Public Shared Sub PrintValue(value As Long)
        Console.WriteLine("PrintValue ({0})", value)
    End Sub

    Public Shared Sub PrintValue(value As String)
        Console.WriteLine("PrintValue ""{0}"")", value)
    End Sub

    Public Shared Sub PrintNumber(value As Double)
        Console.WriteLine("PrintNumber ({0})", value)
    End Sub
End Class

同じ名前の複数のメンバーを使用できる場合は、オーバーロードの解決が必要です。 Binder.BindToMethodメソッドとBinder.BindToField メソッドは、1 つのメンバーへのバインドを解決するために使用されます。 Binder.BindToMethod では、 get および set プロパティ アクセサーを介してプロパティ解決も提供されます。

BindToMethod は呼び出し可能な MethodBase を返しますが、そのような呼び出しが不可能な場合は null 参照 (Visual Basic の場合は Nothing) を返します。 MethodBase戻り値は、通常の場合ですが、match パラメーターに含まれている値の 1 つである必要はありません。

ByRef 引数が存在する場合、呼び出し元はそれらを返すことができます。 したがって、 Binder では、引数の配列を操作した場合に、クライアントが引数の配列 BindToMethod 元の形式にマップし直すことができます。 これを行うには、呼び出し元が引数の順序が変更されていないことを保証する必要があります。 引数が名前で渡されると、 Binder は引数配列を並べ替え、それが呼び出し元に表示されます。 詳細については、Binder.ReorderArgumentArrayを参照してください。

使用可能なメンバーのセットは、型または任意の基本型で定義されているメンバーです。 BindingFlagsが指定されている場合、任意のアクセシビリティのメンバーがセットに返されます。 BindingFlags.NonPublicが指定されていない場合、バインダーはアクセシビリティ規則を適用する必要があります。 PublicまたはNonPublicバインド フラグを指定する場合は、InstanceまたはStaticバインド フラグも指定する必要があります。または、メンバーは返されません。

指定された名前のメンバーが 1 つだけの場合、コールバックは必要なく、そのメソッドに対してバインドが実行されます。 コード例のケース 1 は、この点を示しています。使用できる PrintBob メソッドは 1 つだけであるため、コールバックは必要ありません。

使用可能なセットに複数のメンバーがある場合は、これらすべてのメソッドが BindToMethodに渡され、適切なメソッドが選択されて返されます。 コード例のケース 2 には、 PrintValueという名前の 2 つのメソッドがあります。 適切なメソッドは、 BindToMethodの呼び出しによって選択されます。

ChangeType は、実際の引数を選択したメソッドの仮引数の型に変換する引数強制変換 (型変換) を実行します。 ChangeType は、型が正確に一致する場合でも、すべての引数に対して呼び出されます。

コード例のケース 3 では、値が "5.5" の String 型の実際の引数が、 Double型の仮引数を持つメソッドに渡されます。 呼び出しが成功するには、文字列値 "5.5" を double 値に変換する必要があります。 ChangeType は、この変換を実行します。

ChangeType は、次の表に示すように、無損失または拡張型強制変換のみを実行します。

送信元の種類 対象の種類
任意の型 その基本型
任意の型 実装するインターフェイス
イワナ UInt16、UInt32、Int32、UInt64、Int64、Single、Double
バイト Char、UInt16、Int16、UInt32、Int32、UInt64、Int64、Single、Double
SByte Int16、Int32、Int64、Single、Double
UInt16 UInt32、Int32、UInt64、Int64、Single、Double
Int16 Int32、Int64、Single、Double
UInt32 UInt64、Int64、Single、Double
Int32 Int64、Single、Double
UInt64 Single、Double
Int64 Single、Double
シングル ダブル
非参照型 参照の種類

Type クラスには、Get型のパラメーターを使用して特定のメンバーへの参照を解決するBinderメソッドがあります。 Type.GetConstructorType.GetMethod、および Type.GetProperty 、そのメンバーの署名情報を指定して、現在の型の特定のメンバーを検索します。 Binder.SelectMethodBinder.SelectProperty が呼び出され、適切なメソッドの指定されたシグネチャ情報が選択されます。

こちらも参照ください