Freigeben über


Dynamisches Laden und Verwenden von Typen

Reflection bietet Infrastruktur, die von Sprachcompilern verwendet wird, um implizite späte Bindung zu implementieren. Die Bindung ist der Prozess der Suche nach der Deklaration (d. h. der Implementierung), die einem eindeutig angegebenen Typ entspricht. Wenn dieser Prozess zur Laufzeit statt zur Kompilierungszeit auftritt, wird er als späte Bindung bezeichnet. Visual Basic ermöglicht Es Ihnen, implizite späte Bindung in Ihrem Code zu verwenden. Der Visual Basic-Compiler ruft eine Hilfsmethode auf, die Spiegelung verwendet, um den Objekttyp abzurufen. Die an die Hilfsmethode übergebenen Argumente führen dazu, dass die entsprechende Methode zur Laufzeit aufgerufen wird. Diese Argumente sind die Instanz (ein Objekt), für die die Methode aufgerufen werden soll, den Namen der aufgerufenen Methode (eine Zeichenfolge) und die Argumente, die an die aufgerufene Methode (ein Array von Objekten) übergeben werden.

Im folgenden Beispiel verwendet der Visual Basic-Compiler implizit Spiegelung, um eine Methode für ein Objekt aufzurufen, dessen Typ zur Kompilierungszeit nicht bekannt ist. Eine HelloWorld Klasse verfügt über eine PrintHello Methode, mit der "Hello World" mit einem Text gedruckt wird, der an die PrintHello Methode übergeben wird. Die PrintHello in diesem Beispiel aufgerufene Methode ist tatsächlich ein Type.InvokeMember; der Visual Basic-Code ermöglicht es, die PrintHello Methode aufzurufen, als ob der Typ des Objekts (helloObj) zur Kompilierungszeit (frühe Bindung) und nicht zur Laufzeit (späte Bindung) bekannt war.

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

Benutzerdefinierte Bindung

Zusätzlich zur impliziten Verwendung durch Compiler für späte Bindung kann die Spiegelung explizit im Code verwendet werden, um späte Bindung zu erreichen.

Die Common Language Runtime unterstützt mehrere Programmiersprachen, und die Bindungsregeln dieser Sprachen unterscheiden sich. Im früh gebundenen Fall können Codegeneratoren diese Bindung vollständig steuern. In späterer Bindung durch Spiegelung muss die Bindung jedoch durch eine angepasste Bindung gesteuert werden. Die Binder Klasse bietet benutzerdefinierte Steuerung der Elementauswahl und des Aufrufs.

Mithilfe einer benutzerdefinierten Bindung können Sie eine Assembly zur Laufzeit laden, Informationen zu Typen in dieser Assembly abrufen, den gewünschten Typ angeben und dann Methoden oder Zugriffsfelder oder Eigenschaften für diesen Typ aufrufen. Diese Technik ist nützlich, wenn Sie den Typ eines Objekts zur Kompilierungszeit nicht kennen, z. B. wenn der Objekttyp von der Benutzereingabe abhängig ist.

Im folgenden Beispiel wird ein einfacher benutzerdefinierter Ordner veranschaulicht, der keine Argumenttypkonvertierung bereitstellt. Code für Simple_Type.dll das Hauptbeispiel. Stellen Sie sicher, dass Sie einen Simple_Type.dll Verweis darauf erstellen und dann zur Erstellungszeit in das Projekt aufnehmen.

// 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 und CreateInstance

Wird Type.InvokeMember verwendet, um ein Element eines Typs aufzurufen. Die CreateInstance Methoden verschiedener Klassen, z Activator.CreateInstance . B. und Assembly.CreateInstance, sind spezielle Formen, mit InvokeMember denen neue Instanzen des angegebenen Typs erstellt werden. Die Binder Klasse wird für die Überladungsauflösung und die Argumentkoersion in diesen Methoden verwendet.

Das folgende Beispiel zeigt die drei möglichen Kombinationen von Argumentkoersion (Typkonvertierung) und Elementauswahl. In Fall 1 ist keine Argumentkoersion oder Memberauswahl erforderlich. In Fall 2 ist nur eine Elementauswahl erforderlich. In Fall 3 ist nur die Koersion des Arguments erforderlich.

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

Die Überladungsauflösung ist erforderlich, wenn mehrere Member mit demselben Namen verfügbar sind. Die Binder.BindToMethod Methoden und Binder.BindToField Methoden werden verwendet, um die Bindung an ein einzelnes Element aufzulösen. Binder.BindToMethod stellt außerdem die Eigenschaftsauflösung über die get Accessoren und set Eigenschaftsaccessoren bereit.

BindToMethod gibt den MethodBase Aufruf oder einen Nullverweis (Nothing in Visual Basic) zurück, wenn kein solcher Aufruf möglich ist. Der MethodBase Rückgabewert muss nicht einer der im Match-Parameter enthaltenen Werte sein, obwohl dies der übliche Fall ist.

Wenn ByRef-Argumente vorhanden sind, kann der Aufrufer sie zurückholen. Ermöglicht es einem Client daher, das Array von Argumenten wieder seinem ursprünglichen Formular zuzuordnen, Binder wenn BindToMethod das Argumentarray geändert wurde. Dazu muss der Aufrufer garantiert werden, dass die Reihenfolge der Argumente unverändert ist. Wenn Argumente anhand des Namens übergeben werden, Binder ordnen Sie das Argumentarray neu an, und das sieht der Aufrufer. Weitere Informationen finden Sie unter Binder.ReorderArgumentArray.

Die Gruppe der verfügbaren Member sind die Elemente, die im Typ oder einem beliebigen Basistyp definiert sind. Wenn BindingFlags angegeben, werden Elemente der Barrierefreiheit in der Gruppe zurückgegeben. Wenn BindingFlags.NonPublic nicht angegeben, muss der Ordner Barrierefreiheitsregeln erzwingen. Wenn Sie das Flag oder Public die NonPublic Bindung angeben, müssen Sie auch das Flag oder Instance die Static Bindung angeben, oder es werden keine Member zurückgegeben.

Wenn nur ein Element des angegebenen Namens vorhanden ist, ist kein Rückruf erforderlich, und die Bindung erfolgt für diese Methode. Der Fall 1 des Codebeispiels veranschaulicht diesen Punkt: Es ist nur eine PrintBob Methode verfügbar, und daher ist kein Rückruf erforderlich.

Wenn mehrere Member im verfügbaren Satz vorhanden sind, werden alle diese Methoden übergeben BindToMethod, an die die entsprechende Methode ausgewählt und zurückgegeben wird. In Fall 2 des Codebeispiels gibt es zwei Methoden mit dem Namen PrintValue. Die entsprechende Methode wird durch den Aufruf von BindToMethod.

ChangeType führt die Koersion des Arguments (Typkonvertierung) durch, die die tatsächlichen Argumente in den Typ der formalen Argumente der ausgewählten Methode konvertiert. ChangeType wird für jedes Argument aufgerufen, auch wenn die Typen exakt übereinstimmen.

In Case 3 of the code example, an a actual argument of type String with a value of "5.5" is passed to a method with a formal argument of type Double. Damit der Aufruf erfolgreich ist, muss der Zeichenfolgenwert "5,5" in einen doppelten Wert konvertiert werden. ChangeType führt diese Konvertierung aus.

ChangeType führt nur verlustlose oder verbreiternde Koercionen aus, wie in der folgenden Tabelle dargestellt.

Quelltyp Zieltyp
Beliebiger Typ Der Basistyp
Beliebiger Typ Schnittstelle, die implementiert wird
Verkohlen UInt16, UInt32, Int32, UInt64, Int64, Single, Double
Byte 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 Einzelzimmer, Doppelzimmer
Int64 Einzelzimmer, Doppelzimmer
Ledig Doppelt
Nichtreference-Typ Bezugstyp

Die Type Klasse verfügt über Get Methoden, die Parameter vom Typ Binder verwenden, um Verweise auf ein bestimmtes Element aufzulösen. Type.GetConstructor, Type.GetMethodund Type.GetProperty suchen Sie nach einem bestimmten Element des aktuellen Typs, indem Sie Signaturinformationen für dieses Mitglied bereitstellen. Binder.SelectMethod und Binder.SelectProperty werden wieder aufgerufen, um die angegebenen Signaturinformationen der entsprechenden Methoden auszuwählen.

Siehe auch