Dynamically Loading and Using Types

Reflection provides infrastructure used by language compilers such as Microsoft Visual Basic 2005 and JScript to implement implicit late binding. Binding is the process of locating the declaration (that is, the implementation) that corresponds to a uniquely specified type. When this process occurs at run time rather than at compile time, it is called late binding. Visual Basic 2005 allows you to use implicit late binding in your code; the Visual Basic compiler calls a helper method that uses reflection to obtain the object type. The arguments passed to the helper method cause the appropriate method to be invoked at run time. These arguments are the instance (an object) on which to invoke the method, the name of the invoked method (a string), and the arguments passed to the invoked method (an array of objects).

In the following example, the Visual Basic compiler uses reflection implicitly to call a method on an object whose type is not known at compile time. A HelloWorld class has a PrintHello method that prints out "Hello World" concatenated with some text that is passed to the PrintHello method. The PrintHello method called in this example is actually a Type.InvokeMember; the Visual Basic code allows the PrintHello method to be invoked as if the type of the object (helloObj) were known at compile time (early binding) rather than at run time (late binding).

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

In addition to being used implicitly by compilers for late binding, reflection can be used explicitly in code to accomplish late binding.

The common language runtime supports multiple programming languages, and the binding rules of these languages differ. In the early-bound case, code generators can completely control this binding. However, in late binding through reflection, binding must be controlled by customized binding. The Binder class provides custom control of member selection and invocation.

Using custom binding, you can load an assembly at run time, obtain information about types in that assembly, specify the type that you want, and then invoke methods or access fields or properties on that type. This technique is useful if you do not know an object's type at compile time, such as when the object type is dependent on user input.

The following example demonstrates a simple custom binder that provides no argument type conversion. Code for Simple_Type.dll precedes the main example. Be sure to build Simple_Type.dll and then include a reference to it in the project at build time.

' Code for building SimpleType.dll. 
Imports System
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

Use Type.InvokeMember to invoke a member of a type. The CreateInstance methods of various classes, such as System.Activator and System.Reflection.Assembly, are specialized forms of InvokeMember that create new instances of the specified type. The Binder class is used for overload resolution and argument coercion in these methods.

The following example shows the three possible combinations of argument coercion (type conversion) and member selection. In Case 1, no argument coercion or member selection is needed. In Case 2, only member selection is needed. In Case 3, only argument coercion is needed.

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

Overload resolution is needed when more than one member with the same name is available. The Binder.BindToMethod and Binder.BindToField methods are used to resolve binding to a single member. Binder.BindToMethod also provides property resolution through the get and set property accessors.

BindToMethod returns the MethodBase to invoke, or a null reference (Nothing in Visual Basic) if no such invocation is possible. The MethodBase return value need not be one of those contained in the match parameter, although that is the usual case.

When ByRef arguments are present, the caller might want to get them back. Therefore, Binder allows a client to map the array of arguments back to its original form if BindToMethod has manipulated the argument array. In order to do this, the caller must be guaranteed that the order of the arguments is unchanged. When arguments are passed by name, Binder reorders the argument array, and that is what the caller sees. For more information, see Binder.ReorderArgumentArray.

The set of available members are those members defined in the type or any base type. If BindingFlags.NonPublic is specified, members of any accessibility will be returned in the set. If BindingFlags.NonPublic is not specified, the binder must enforce accessibility rules. When specifying the Public or NonPublic binding flag, you must also specify the Instance or Static binding flag, or no members will be returned.

If there is only one member of the given name, no callback is necessary, and binding is done on that method. Case 1 of the code example illustrates this point: Only one PrintBob method is available, and therefore no callback is needed.

If there is more than one member in the available set, all these methods are passed to BindToMethod, which selects the appropriate method and returns it. In Case 2 of the code example, there are two methods named PrintValue. The appropriate method is selected by the call to BindToMethod.

ChangeType performs argument coercion (type conversion), which converts the actual arguments to the type of the formal arguments of the selected method. ChangeType is called for every argument even if the types match exactly.

In Case 3 of the code example, an actual argument of type String with a value of "5.5" is passed to a method with a formal argument of type Double. For the invocation to succeed, the string value "5.5" must be converted to a double value. ChangeType performs this conversion.

ChangeType performs only lossless or widening coercions, as shown in the following table.

Source type

Target type

Any type

Its base type

Any type

Interface it implements

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

Nonreference type

Reference type

The Type class has Get methods that use parameters of type Binder to resolve references to a particular member. Type.GetConstructor, Type.GetMethod, and Type.GetProperty search for a particular member of the current type by providing signature information for that member. Binder.SelectMethod and Binder.SelectProperty are called back on to select the given signature information of the appropriate methods.

Was this page helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2015 Microsoft