C#反射教程(5)

在目录下新建一个程序文件,并命名为LateBinding.cs,编写代码如代码7.12所示。

代码7.12 晚期绑定:LateBinding.cs

+展开
-C#
using System;
//导入相应的命名空间
using System.Reflection;
using System.IO;

class  LateBinding
{
static  void Main( string[] args)
{
Console.Write( "/n【1】请输入传递给OldClass类Method静态方法的参数:");
string inputA = Console.ReadLine();
Console.Write( "/n【2】请输入传递给NewClass类Method方法的参数:");
string inputB = Console.ReadLine();
Console.Write( "/n【3】请输入传递给MyClass类Method方法的参数:");
string inputC = Console.ReadLine();
Console.WriteLine("/n/t=======以下是不同程
序集的不同方法调用结果=======/n");
try
{
//将用户输入的3组值传递给以下3个方法
LoadOldClass(inputA);
LoadNewClass(inputB);
LoadMyClass(inputC);
}
//捕获文件未找到异常
catch (FileNotFoundException e)
{
//输出异常信息
Console.WriteLine( "异常信息:{0}", e.Message);
}
//捕获一般异常
catch (Exception e)
{
//输出异常信息
Console.WriteLine( "异常信息:{0}", e.Message);
}   
}
//定义LoadOldClass方法,接收一个string类型参数
//该类用于加载OldClass程序集,并调用该程序集中OldClass类的Method静态方法
static  void LoadOldClass( string input)
{
//调用Assembly的Load方法,载入OldClass程序集
Assembly am = Assembly.Load( "OldClass");
//获取OldClass类的Type对象
Type OldTp = am.GetType( "OldClass"falsefalse);
//定义仅一个参数的数组OldList,子项初始化为input参数
string[] OldList =  new  string[1] { input };
//调用OldTp的InvokeMember方法,传递相应的参数
//将方法返回结果转换为string类型并赋值给txt变量
string txt = ( string)OldTp.InvokeMember(
"Method",
BindingFlags.InvokeMethod,
null,
null,
OldList
);
//输出txt变量
Console.WriteLine(txt);
}
//定义LoadNewClass方法,接收一个string类型参数
//该类用于加载NewClass程序集,并调用该程序集中NewClass类对象的Method实例方法
static  void LoadNewClass( string input)
{
//创建当前应用程序域的指定程序集中指定类型的实例
object obj = AppDomain.CurrentDomain.
CreateInstanceAndUnwrap( "NewClass""NewClass");
//调用obj的GetType方法,获取Type对象
Type Newtp = obj.GetType();
//定义仅一个参数的数组NewList,子项初始化为input参数
string[] NewList =  new  string[1] { input };
//调用Newtp的InvokeMember方法,传递相应的参数
//将方法返回结果转换为string类型并赋值给txt变量
string txt = ( string)Newtp.InvokeMember(
"Method",
BindingFlags.InvokeMethod,
null,
obj,
NewList
);
//输出txt变量
Console.WriteLine(txt);
}
//定义LoadMyClass方法,接收一个string类型参数
//该类用于加载MyClass程序集,并调用该程序集中MyClass类对象的Method实例方法
static  void LoadMyClass( string input)
{
//定义仅一个参数的数组MyList,子项初始化为input参数
string[] MyList =  new  string[1] { input };
//调用Assembly的Load方法,载入MyClass程序集
Assembly MyAm = Assembly.Load( "MyClass");
//获取MyClass类的Type对象
Type MyTp = MyAm.GetType( "MyClass", false, false);
//调用Activator类的CreateInstance方法
//该方法可创建参数类型的实例
object MyObj = Activator.CreateInstance(MyTp);
//搜索MyTp的参数指定方法,并返回给mi变量
MethodInfo mi = MyTp.GetMethod( "Method");
//根据参数调用mi的方法,并将返回结果赋值给txt变量
string txt = ( string)mi.Invoke(MyObj, MyList);
//输出txt变量
Console.WriteLine(txt);
mi = MyTp.GetMethod( "MethodTxt");
//根据参数调用mi的方法,并将返回结果赋值给txt变量
txt = ( string)mi.Invoke(MyObj,  null);
//输出txt变量
Console.WriteLine(txt);
}
}


  在命令行下将OldClass.cs、NewClass.cs和MyClass.cs编译为dll程序集,如图7.14所示。

图7.14 编译外部程序集

  在命令行下编译LateBinding.cs,执行LateBinding程序,运行结果如图7.15所示。

图7.15 晚期绑定
  本程序以多种情况展示了晚期绑定的编写方法,主程序的3大部分分别封装在LoadOldClass()、LoadNewClass()和 LoadMyClass()静态方法中。考虑到外部程序集有可能不存在,主程序编写了"文件未找到"的异常捕捉。LoadOldClass()和 LoadNewClass()方法的晚期绑定通过InvokeMember()方法调用对象的成员,而LoadMyClass()方法则直接通过如下代码完成方法的调用。

+展开
-C#
MethodInfo mi = MyTp.GetMethod( "Method");
string txt = ( string)mi.Invoke(MyObj, MyList); 


  首先第1行代码从Type类型中获取参数所指定名称的方法,并返回MethodInfo类型的对象mi,然后调用mi的Invoke()方法,即可执行Method()方法。Invoke()方法的第1个参数调用Method()方法的对象,第2个参数传递给方法的参数数组,如果Method()方法没有参数,可以使用null。

解析

  由于没有在当前程序集的清单中列出动态加载的外部程序集,所以编译时编译器并不知道该程序集是否存在。如果在程序中创建该程序集指定类型的实例,并调用其成员,这就是晚期绑定技术。晚期绑定可以使用System.Activator类,如以下代码所示:

+展开
-C#
using System;
using System.Reflection;

Assembly am = Assembly.Load( "外部程序集名称");
Type tp = am .GetType( "MyClass", false, false);
object obj = Activator.CreateInstance(tp);


  以上代码创建了MyClass类的对象引用obj,如果需要调用其成员,不能直接用点符号调用,因为该对象为object类型,而不是指定类型。调用其成员可以使用该类型Type对象的InvokeMember()方法,并根据多个参数确定所调用的成员信息。例如, obj对象的Method()方法接收string类型的一个参数,并且返回类型为string。如果调用obj对象的Method()方法,方法代码如下所示:

+展开
-C#
string[] Param =  new  string[1] {  "参数值" };
string txt = ( string)OldTp.InvokeMember(
"Method", //成员名称
BindingFlags.InvokeMethod, //绑定标记,
null, //绑定对象,
obj, //所调用对象
Param //所传递参数数组
);


  以上代码中的InvokeMember()方法的返回值即为Method()方法的返回值,由于InvokeMember()方法返回值是 object类型,所以需要强制转换为string类型再赋值给txt。InvokeMember()方法的第1个参数为字符串类型,代表所调用成员的名称;第2个参数为绑定标记,代表成员类型,该值需要访问BindingFlags类,例如成员方法BindingFlags.InvokeMethod;第3个参数为绑定对象,一般使用null,代表使用默认绑定器,即System.Type.DefaultBinder类对象;第4个参数为所调用的对象 obj,如果调用静态方法,该参数为null。第4个参数传递给方法参数数组,例如代码中的Param。

  创建外部程序集指定类型的实例也可调用当前应用程序域的CreateInstanceAndUnwrap()方法,如以下代码所示:

+展开
-C#

object obj = AppDomain.CurrentDomain.
CreateInstanceAndUnwrap( "程序集名称""类型名称");



面试例题10:如何通过晚期绑定读写属性和字段成员?

考点:InvokeMember()方法读写属性和字段成员以及Invoke()方法读写属性和字段成员。

出现频率:★★★

解答

  晚期绑定读写属性和字段成员均可以使用晚期绑定对象的InvokeMember()方法实现,也可以使用所指定类型的Type对象反射实现。本题在 LateBindingOther.cs代码中,主程序读写Human.dll程序集中Person类的Name属性和_age字段。在目录下新建一个程序文件,并命名为Person.cs,编写代码如代码7.13所示。

代码7.13 外部Person类:Person.cs

+展开
-C#
using System;

class  Person
{       
//定义两个字段,其中_name字段由Name属性读写
string _name;
//定义public的int类型的_age字段
public  int _age = 0;
public  string Name
{
get
{
return _name;
}
set
{
_name =  value;
}
}
}


  在目录下新建一个程序文件,并命名为LateBindingOther.cs,编写代码如代码7.14所示。

代码7.14 晚期绑定读写属性和字段成员:LateBindingOther.cs

+展开
-C#
using System;
//导入相应的命名空间
using System.Reflection;
using System.IO;

class  LateBindingOther
{
static  void Main( string[] args)
{
try
{
//接收用户输入的两组值,并传递给ClassOP方法
Console.Write( "/n【1】请输入需写入Person的属性值:");
string inputA = Console.ReadLine();
Console.Write( "/n【2】请输入需写入Person的字段值:");
int inputB = System.Convert.ToInt32(Console.ReadLine());
Console.WriteLine("/n/t=======以下是属
性和字段被写入后读出的结果=======/n");
ClassOP(inputA, inputB);
}
//捕获输入格式异常
catch (FormatException e)
{
//输出异常信息
Console.WriteLine( "异常信息:{0}", e.Message);
}
//捕获文件未找到异常
catch (FileNotFoundException e)
{
//输出异常信息
Console.WriteLine( "异常信息:{0}", e.Message);
}
//捕获一般异常
catch (Exception e)
{
//输出异常信息
Console.WriteLine( "异常信息:{0}", e.Message);

}
static  void ClassOP( string a,  int b)
{
//调用Assembly的Load方法,载入Human程序集
Assembly am = Assembly.Load( "Human");
//获取Person类的Type对象
Type tp = am.GetType( "Person"falsefalse);
//调用Activator类的CreateInstance方法
//该方法可创建参数类型的实例
object obj = Activator.CreateInstance(tp);
//写入Name属性值
tp.InvokeMember( "Name", BindingFlags.SetProperty, 
null, obj,  new  string[] { a });
//读取Name属性值
string name = ( string)tp.InvokeMember( "Name"
BindingFlags.GetProperty,  null, obj,  null);
Console.WriteLine( "Person类的Name属性值为:【{0}】", name);
//写入_age字段值
tp.InvokeMember( "_age", BindingFlags.SetField,
null, obj,  new  object[] { b });
//读取_age字段值
int age = ( int)tp.InvokeMember( "_age"
BindingFlags.GetField,  null, obj,  null);
Console.WriteLine( "Person类的_age字段值为:【{0}】", age);
}
}


  在命令行下将Person.cs编译为Human.dll程序集,编译LateBindingOther.cs,执行LateBindingOther程序。程序将提示"【1】请输入需写入Person的属性值:"和"【2】请输入需写入Person的字段值:",分别输入"比尔"和"50",运行结果如图7.16所示。

图7.16 晚期绑定读写属性和字段成员

  本程序采用InvokeMember()方法成功地读写了Person类的Name属性和_age字段,也可以使用另一种方法(Invoke()方法)完成相同的功能。

解析

  类似于晚期绑定调用方法,读写属性和字段成员可以用相同的方法实现。假设指定类型的Type对象为tp,晚期绑定所创建该类型对象为obj,使用InvokeMember()方法读写string类型的属性成员如以下代码所示:

+展开
-C#
//写入属性
tp.InvokeMember( "属性名称", BindingFlags.SetProperty,  null, obj, 
属性值数组(object类型));
//读取属性值到txt变量
string txt = ( string)tp.InvokeMember( "属性名称"
BindingFlags.GetProperty,  null, obj,  null);
以上代码通过编写不同的绑定标记,访问BindingFlags类的成员,
设定obj对象成员的操作类型及方式。如果不用InvokeMember()方法,
也可选用Invoke()方法解决,如以下代码所示:
//获取指定属性的PropertyInfo类型对象p
PropertyInfo p = tp.GetProperty( "属性名称");
//写入属性
p.SetValue(obj,属性值数组, null);
//读取属性值到txt变量
string txt = ( string)p.GetValue(obj, null);


  该方法更为简单,也比较直观,SetValue()方法和GetValue()方法最后一个参数为索引化属性值的索引值,如果不是索引化属性值则取null。

你可能感兴趣的:(C#)