Dynamisches Laden und Verwenden von Typen

Reflektion stellt die Infrastruktur zur Verfügung, die Sprachcompiler wie Microsoft Visual Basic 2005 oder JScript verwenden, um implizite späte Bindung zu implementieren. Bindung besteht im Auffinden der Deklaration (d. h. der Implementierung), die sich auf einen eindeutig festgelegten Typ bezieht. Wenn dieser Prozess nicht während der Kompilierzeit, sondern während der Laufzeit stattfindet, wird er als späte Bindung bezeichnet. Visual Basic 2005 lässt die Verwendung der impliziten späten Bindung im Code zu. Der Visual Basic-Compiler ruft eine Hilfsmethode auf, die unter Verwendung von Reflektion den Objekttyp abruft. Die an die Hilfsmethode übergebenen Argumente sorgen dafür, dass die richtige Methode zur Laufzeit aufgerufen wird. Es handelt sich um folgende Argumente: die Instanz (ein Objekt), für das die Methode aufgerufen werden soll, der Name der aufgerufenen Methode (eine Zeichenfolge) sowie die Argumente, die der aufgerufenen Methode übergeben werden (ein Objektarray).

Im folgenden Beispiel verwendet der Visual Basic-Compiler Reflektion, um implizit eine Methode für ein Objekt aufzurufen, dessen Typ zum Zeitpunkt der Kompilierung nicht bekannt ist. Eine HelloWorld-Klasse enthält eine PrintHello-Methode, die "Hello World" zusammen mit anderem, an sie übergebenen Text ausgibt. Die in diesem Beispiel aufgerufene PrintHello-Methode ist eigentlich ein Type.InvokeMember; im Visual Basic-Code kann die PrintHello-Methode so aufgerufen werden, als sei der Objekttyp bereits zur Kompilierungszeit (frühes Binden) und nicht erst zur Laufzeit (spätes Binden) bekannt.

Imports System
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

Benutzerdefiniertes Binden

Reflektion kann nicht nur von Compilern implizit für spätes Binden eingesetzt werden, sondern auch explizit im Code.

Common Language Runtime unterstützt mehrere Programmiersprachen; die Bindungsregeln dieser Sprachen unterscheiden sich voneinander. Im Fall früher Bindung können Code-Generatoren den Bindungsvorgang vollständig kontrollieren. Im Fall später Bindung durch Reflektion muss der Vorgang durch benutzerdefinierte Bindung gesteuert werden. Die Binder-Klasse stellt benutzerdefinierte Steuerung für die Auswahl und den Aufruf von Membern bereit.

Mit benutzerdefinierter Bindung können Sie eine Assembly zur Laufzeit laden, Informationen über Typen in dieser Assembly erhalten, den gewünschten Typ festlegen und anschließend für diesen Typ Methoden aufrufen oder auf Felder und Eigenschaften zugreifen. Diese Technik ist besonders nützlich, wenn ein Objekttyp zur Kompilierungszeit nicht bekannt ist, z. B. weil der Typ von Benutzereingaben abhängt.

Das folgende Beispiel veranschaulicht einen einfachen benutzerdefinierten Binder, der keine Argumenttypkonvertierung durchführt. Dem Hauptbeispiel geht Code für Simple_Type.dll voran. Stellen Sie sicher, dass Sie Simple_Type.dll erstellen und dann in das Projekt zur Erstellungszeit einen Verweis darauf aufnehmen.

' Code for building Simple_Type.dll.
Imports System

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

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

Imports System
Imports System.Reflection
Imports System.Globalization
Imports Simple_Type.Simple_Type

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 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 'Main
    End Class 'MyMainClass

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

        Public Overrides Function BindToMethod(ByVal bindingAttr As 
            BindingFlags, ByVal match() As MethodBase, ByRef args() As 
                Object, ByVal modifiers() As ParameterModifier, ByVal 
                    culture As CultureInfo, ByVal 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.
            Dim mb As MethodBase
            For Each mb In match
                Dim parameters As ParameterInfo() = mb.GetParameters()
                If ParametersMatch(parameters, args) Then
                    Return mb
                End If
            Next mb
            Return Nothing
        End Function 'BindToMethod

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

        Public Overrides Function SelectMethod(ByVal bindingAttr As 
            BindingFlags, ByVal match() As MethodBase, ByVal types() As 
                Type, ByVal 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.
            Dim mb As MethodBase
            For Each mb In match
                Dim parameters As ParameterInfo() = mb.GetParameters()
                If ParametersMatch(parameters, types) Then
                    Return mb
                End If
            Next mb
            Return Nothing
        End Function 'SelectMethod

        Public Overrides Function SelectProperty(ByVal bindingAttr As 
            BindingFlags, ByVal match() As PropertyInfo, ByVal returnType 
                As Type, ByVal indexes() As Type, ByVal modifiers() As 
                    ParameterModifier) As PropertyInfo
            If match Is Nothing Then
                Throw New ArgumentNullException("match")
            End If
            Dim pi As PropertyInfo
            For Each pi In match
                If pi.GetType() Is returnType And 
                    ParametersMatch(pi.GetIndexParameters(), indexes) Then
                    Return pi
                End If
            Next pi
            Return Nothing
        End Function 'SelectProperty

        Public Overrides Function ChangeType(ByVal value As Object, 
            ByVal myChangeType As Type, ByVal 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
            End Try
        End Function 'ChangeType

        Public Overrides Sub ReorderArgumentArray(ByRef args() As Object, 
            ByVal 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 'ReorderArgumentArray

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

        ' Returns true only if the type of each object in a matches
        ' the type of each corresponding entry in b.
        Private Overloads Function ParametersMatch(ByVal a() As 
            ParameterInfo, ByVal b() As Type) As Boolean
            If a.Length <> b.Length Then
                Return False
            End If
            Dim i As Integer
            For i = 0 To a.Length - 1
                If Not (a(i).ParameterType Is b(i)) Then
                    Return False
                End If
            Next i
            Return True
        End Function 'ParametersMatch
    End Class 'MyCustomBinder
End Namespace 'Custom_Binder

// Code for building SimpleType.dll.
using System;

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);
        }
    }
}


using System;
using System.Reflection;
using System.Globalization;
using Simple_Type;
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;
        }
    }
}

InvokeMember und CreateInstance

Verwenden Sie Type.InvokeMember, um einen Member eines Typs aufzurufen. Die CreateInstance-Methoden verschiedener Klassen, z. B. System.Activator und System.Reflection.Assembly, sind spezielle Formen von InvokeMember, die neue Instanzen des angegebenen Typs erzeugen. Die Binder-Klasse wird für Überladungsauflösung und Argument-Coertion in diesen Methoden verwendet.

Im folgenden Beispiel werden die drei möglichen Kombinationen von Argument-Coertion (Typkonvertierung) und Memberauswahl dargestellt. Fall 1: Argument-Coertion oder Memberauswahl wird nicht benötigt. Fall 2: Nur Memberauswahl wird benötigt. Fall 3: Nur Argument-Coertion wird benötigt.

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

    // 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 ({0})", value);
    }
    public static void PrintValue (String value)
    {
        Console.WriteLine ("PrintValue\"{0}\")", value);
    }
   
    public static void PrintNumber (double value)
    {
        Console.WriteLine ("PrintNumber ({0})", value);
    }
}

Überladungsauflösung wird dann benötigt, wenn mehrere Member gleichen Namens zur Verfügung stehen. Die Binder.BindToMethod-Methode und die Binder.BindToField-Methode werden verwendet, um Bindungen an einen einzelnen Member aufzulösen. Mit Binder.BindToMethod können Eigenschaften durch die Eigenschaften-Accessoren get und set aufgelöst werden.

BindToMethod gibt MethodBase zum Aufruf zurück. Ist ein solcher Aufruf nicht möglich, wird ein NULL-Verweis (in Visual Basic Nothing) zurückgegeben. Der Rückgabewert von MethodBase ist nicht unbedingt im match-Parameter enthalten, obwohl dies der Normalfall ist.

Sind ByRef-Argumente vorhanden, könnte der Aufrufer die Argumente zurückverlangen. Aus diesem Grund erlaubt Binder einem Client, das Argumentarray seinem ursprünglichen Zustand zuzuordnen, falls das Array durch BindToMethod verändert wurde. Zu diesem Zweck muss dem Aufrufer gewährleistet werden, dass die Reihenfolge der Argumente unverändert bleibt. Wenn Argumente nach Namen übergeben werden, erstellt Binder eine neue Anordnung des Argumentarrays. Nur dieses ist für den Aufrufer sichtbar. Weitere Informationen finden Sie unter der Binder.ReorderArgumentArray-Methode.

Die Menge verfügbarer Member besteht aus den im Typ oder einem beliebigen Basistyp definierten Membern. Ist BindingFlags.NonPublic angegeben, werden Member unabhängig von ihrer Zugänglichkeit an die Menge zurückgegeben. Andernfalls muss der Binder Zugänglichkeitsregeln erzwingen. Wenn die Bindungsflags Public oder NonPublic gesetzt sind, müssen auch die Bindungsflags Instance oder Static gesetzt werden, sonst wird kein Member zurückgegeben.

Gibt es nur einen Member des jeweiligen Namens, ist kein Rückruf erforderlich, und die Bindung wird für diese Methode vorgenommen. Im 1. Fall des Codebeispiels gibt es nur eine verfügbare PrintBob-Methode, daher ist kein Rückruf erforderlich.

Gibt es mehrere Member in der verfügbaren Menge, werden alle diese Methoden an BindToMethod übergeben, wodurch die richtige Methode ausgewählt und zurückgegeben wird. Im 2. Fall des Codebeispiels gibt es zwei Methoden namens PrintValue. Die richtige Methode wird durch einen Aufruf von BindToMethod ausgewählt.

ChangeType führt Argument-Coertion (Typkonvertierung) durch, wobei die aktuellen Argumente in den Typ der formalen Argumente der gewählten Methode konvertiert werden. ChangeType wird für jedes Argument aufgerufen, selbst dann, wenn die Typen exakt übereinstimmen.

Im 3. Fall des Codebeispiels wird ein Argument des Typs String mit dem Wert "5,5" an eine Methode mit einem formalen Argument vom Typ Double übergeben. Für einen erfolgreichen Aufruf muss der Zeichenfolgenwert "5,5" in einen Double-Wert konvertiert werden. Die Konvertierung wird von ChangeType übernommen.

ChangeType führt nur verlustfreie oder erweiternde Coertionen aus, wie in folgender Tabelle beschrieben:

Quelltyp Zieltyp

Beliebiger Typ

Der entsprechende Basistyp.

Beliebiger Typ

Implementierte Schnittstelle

Char

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

Single, Double

Int64

Single, Double

Single

Double

Kein Verweistyp

Verweistyp

Die Type-Klasse enthält Get-Methoden, die mithilfe von Parametern vom Typ Binder Verweise zu einem bestimmten Member auflösen. Type.GetConstructor, Type.GetMethod und Type.GetProperty suchen nach einem bestimmten Member des aktuellen Typs, indem sie die Signaturinformationen für diesen Member zur Verfügung stellen. Binder.SelectMethod und Binder.SelectProperty werden zur Auswahl der jeweiligen Signaturinformationen der entsprechenden Methoden aufgerufen.

Siehe auch

Referenz

Dynamisches Laden und Verwenden von Typen
Type.InvokeMember
Assembly.Load

Konzepte

Anzeigen von Typinformationen
Übersicht über Konvertierung