上善若水
愿倾其毕生之功,高扬.NET旗帜
反射提供语言编译器(如 Microsoft Visual Basic 2005 和 JScript)用于实现隐式后期绑定的基础结构。绑定是查找与唯一指定的类型相对应的声明(即实现)的过程。如果此过程是在运行时而不是在编译时发生,则称其为“后期绑定”。利用 Visual Basic 2005,可以在代码中使用隐式后期绑定;Visual Basic 编译器会调用一个帮助器方法,该方法使用反射来获取对象类型。传递给帮助器方法的参数有助于在运行时调用正确的方法。这些参数包括:对其调用方法的实例(对象),被调用方法的名称(字符串),以及传递给被调用方法的参数(对象数组)。
在下面的示例中,Visual Basic 编译器使用反射隐式地对其类型在编译时未知的对象调用方法。HelloWorld 类具有一个 PrintHello 方法,它输出与传递给 PrintHello 方法的某些文本串联的“Hello World”。在此示例中调用的 PrintHello 方法实际上是 Type..::.InvokeMember;Visual Basic 代码允许按照对象 (helloObj) 的类型在编译时已知(早期绑定)而不是在运行时已知(后期绑定)的方式来调用 PrintHello 方法。
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


自定义绑定

除了由编译器隐式地用来进行后期绑定之外,反射还可以在代码中显式地用来完成后期绑定。公共语言运行库支持多种编程语言,但这些语言的绑定规则各不相同。在早期绑定的情况下,代码生成器可以完全控制此绑定。但是,当通过反射进行后期绑定时,必须用自定义绑定来控制绑定。Binder 类提供了对成员选择和调用的自定义控制。利用自定义绑定,您可以在运行时加载程序集,获取有关该程序集中类型的信息,然后对该类型调用方法或访问该类型的字段或属性。如果您在编译时(例如当对象类型依赖于用户输入时)不知道对象的类型,就可以使用这种方法。下面的示例说明不提供参数类型转换的简单的自定义联编程序。Simple_Type.dll 的代码位于示例主体之前。确保生成 Simple_Type.dll,然后在生成时在项目中包括对它的引用。

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

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

        
public void MyMethod(string strint 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 和 CreateInstance

使用 Type.InvokeMember 可调用类型的成员。各个类(如 System.Activator 和 System.Reflection.Assembly)的 CreateInstance 方法是 InvokeMember 的特殊形式,它们可新建特定类型的实例。Binder 类用于在这些方法中进行重载决策和参数强制。
下面的示例显示参数强制(类型转换)和成员选择的三种可能的组合。在第 1 种情况中,不需要任何参数强制或成员选择。在第 2 种情况中,只需要成员选择。在第 3 种情况中,只需要参数强制

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

}

当多个成员具有相同的名称时,将需要重载决策。Binder..::.BindToMethod 和 Binder..::.BindToField 方法用于解析与单个成员的绑定。Binder.BindToMethod 还通过 get 和 set 属性访问器提供了属性解析。
BindToMethod 返回要调用的 MethodBase;如果无法进行这种调用,则返回 null 引用(在 Visual Basic 中为 Nothing)。虽然 MethodBase 返回值通常是 match 参数中所包含的值之一,但它并不必如此。
当存在 ByRef 参数时,调用方可能需要取回这些参数。因此,如果 BindToMethod 已经操作参数数组,Binder 会允许客户端将参数数组映射回它的初始形式。为了实现这一目的,必须向调用方保证参数的顺序不会改变。当按名称传递参数时,Binder 将重新排列参数数组,这就是调用方所见的参数。有关更多信息,请参见 Binder.ReorderArgumentArray。可用成员集包括在类型和任何基类型中定义的成员。如果指定 BindingFlags.NonPublic,将返回该成员集中具有任何可访问性的成员。如果未指定 BindingFlags.NonPublic,联编程序就必须强制可访问性规则。当指定 Public 或 NonPublic 绑定标志时,还必须指定 Instance 或 Static 绑定标志,否则不会返回任何成员。如果只有一个成员具有给定名称,则不必进行回调,而在该方法上进行绑定。代码示例的第 1 种情况说明了这一点:只有一个 PrintBob 方法可用,因此不需要进行回调。
如果可用集中有多个成员,所有这些方法都将传递给 BindToMethod,它将选择正确的方法并将其返回。在代码示例的第 2 种情况下,有两个名为 PrintValue 的方法。对 BindToMethod 的调用将选择正确的方法。
ChangeType 执行参数强制转换(类型转换),以便将实参转换为选定方法的形参的类型。即使类型完全匹配,也会为每个参数调用 ChangeType。
在代码示例的第 3 种情况下,将类型为 String 值为“5.5”的实参传递给了具有类型为 Double 的形参的方法。要使调用成功,必须将字符串值“5.5”转换为 double 值。ChangeType 会执行此转换。
ChangeType 仅执行无损或扩大强制,如下表所示。

源类型

目标类型

任何类型

它的基类型

任何类型

它所实现的接口

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

非引用类型

引用类型

Type 类具有 Get 方法,这些方法使用 Binder 类型的参数来解析对特定成员的引用。Type.GetConstructor、Type.GetMethod 和 Type.GetProperty 通过为当前类型的特定成员提供签名信息来搜索该成员。对 Binder.SelectMethod 和 Binder.SelectProperty 进行回调以选择相应方法的给定签名信息
posted on 2008-04-28 20:08  小兔快跑  阅读(259)  评论(0编辑  收藏  举报