本章是上一章的继续,再获取到对象类型后,接下来做的事情。
第一部分 动态调用成员——调用方法,检索或更改属性,以及字段
方法1:利用Info类调用类成员
1.用MethodInfo类调用方法:
object[] Invoke(object obj, Object[] parameters)
其中,第1个参数obj,是对象的实例(静态方法相应参数为null);第2个参数parameters是要传递到方法中的参数数组;返回值为object类型,因此,要对结果进行强制类型转换。示例如下:
public
class
MathClass
{
public
int
Multiply(
int
x,
int
y)
{
return
x
*
y;
}
[STAThread]
static
void
Main()
{
MathClass objMath
=
new
MathClass();
object
[] paramArray
=
{
4
,
5
};
MethodInfo method
=
objMath.GetType().GetMethod(
"
Multiply
"
);
int
result
=
(
int
)method.Invoke(objMath, paramArray);
}
}
其中,
objMath.GetType()
.GetMethod(
"
Multiply
"
)方法,默认搜索所有共有成员,可以使用其重载方法:
objMath.GetType()
.GetMethod(
"
Multiply
"
, BindingFlag.NonPublic),从而获取非公有成员。
2.用PropertyInfo类调用属性:
GetValue(Object obj, object[] index)
SetValue(Object obj, Object NewValue, object[] index)
其中,index为索引器(静态设为null),obj为对象的实例(静态设为null),返回值同样为object类型。示例如下:
public
class
Human
{
private
string
strName;
private
string
Name
{
get
{
return
strName; }
set
{ strName
=
value; }
}
[STAThread]
static
void
Main()
{
Human newHuman
=
new
Human();
//
得到私有属性Name类型对象
PropertyInfo prop
=
newHuman.GetType().GetProperty(
"
Name
"
, BindingFlags.Instance
|
BindingFlags.NonPublic);
//
设置私有属性Name
string
param
=
"
Jax.Bao
"
;
prop.SetValue(newHuman, param,
null
);
//
获取私有属性Name
Console.WriteLine(prop.GetValue(newHuman,
null
).ToString());
}
}
3.用FieldInfo类调用字段:
GetValue(Object obj)
SetValue(Object obj, Object value)
FieldInfo类使用同PropertyInfo,只是没有Index罢了。示例略。
方法2:利用InvokeMember()方法调用类成员
3个重载,最常用的如下:
Object InvokeMember(
string
name,
//
要调用的对象名:可以是属性名/方法名/字段名
BindingFlags invokeAttr,
//
搜索成员的条件,以及如何处理第一个参数:
//如BindingFlags.InvokeMethod表示第一个参数为方法名;
//BindingFlags.GetProperty或
SetProperty
表示第一个参数为属性名;
//BindingFlags.SetField或
GetField
表示第一个参数为字段名;
Binder binder,
//
一般设为null,则会使用默认的DefaultBinder,对提供的参数进行类型转换,从原类型转为目标类型
Object target,
//
调用成员的类型的实例(静态成员为null)
Object[] args
//
对方法而言,是方法参数数组;对字段/属性而言,获取时是null,设置时是NewValue
);
1.调用方法:
MethodInfo类的Invoke()方法,不能直接处理重载方法,即无法判断使用哪个重载方法,而InvokeMember则可以自动找到匹配的重载方法。示例如下:
Type mathType
=
typeof
(System.Math);
object
[] paramArray
=
{
5
,
8
};
int
result
=
(
int
)mathType.InvokeMember(
"
Max
"
,
BindingFlags.Public
|
BindingFlags.InvokeMethod
|
BindingFlags.Static,
null
,
null
, paramArray);
当然,如果使用MethodInfo类的Invoke()方法处理重载方法,要辅助以SelectMethod()先找到对应的重载方法,然后才能调用。
2.操纵属性
得到属性用BindingFlags.GetProperty,同时参数数组为null;设置属性用BindingFlags.SetProperty,示例如下:
Type humanType
=
typeof
(Human);
Human newHuman
=
new
Human();
//
设置私有属性Name
object
[] paramArray
=
{
"
Jax.Bao
"
};
humanType.InvokeMember(
"
Name
"
,
BindingFlags.NonPublic
|
BindingFlags.Instance
|
BindingFlags.SetProperty,
null
, newHuman, paramArray);
//
得到私有属性Name类型对象
string
result
=
humanType.InvokeMember(
"
get_Name
"
,
BindingFlags.NonPublic
|
BindingFlags.Instance
|
BindingFlags.InvokeMethod,
null
, newHuman,
null
);
补注:
在MSIL中属性其实就是方法,所以对属性的操纵也可以使用如下方式(以get为例):
humanType.InvokeMember(
"
get_Name
"
,
BindingFlags.NonPublic
|
BindingFlags.Instance
|
BindingFlags.InvokeMethod,
null
, newHuman,
null
);
同理,可以使用MethodInfo.InvokeMethod()改写为:
Human objHuman
=
new
Human();
Object[] paramArray
=
{
"
Jax.Bao
"
};
MethodInfo method
=
objHuman.GetType().GetMethod(
"
get_Name
"
);
string
result
=
(
string
)method.Invoke(objHuman, paramArray);
3.操纵字段
基本上同于"操纵属性",不再多言。
两种方法的比较:
Type的InvokeMember()方法灵活且功能强大,应该优先使用;仅当需要处理元数据时,才使用info类。
第二部分 用反射模拟委托功能
之所以要模拟,是因为委托时类型安全的(编译期)——区别于反射(运行期),所以小型程序要尽量使用委托。
委托不区分静态方法/实例方法——区别于反射。
模拟代码如下:
public
class
Invoker
{
private
Type myType;
private
Object myObject;
private
String myMethod;
public
Invoker(Type targetType, String targetMethod)
{
myType
=
targetType;
myMethod
=
targetMethod;
}
public
Invoker(Object targetObject, String targetMethod)
{
myObject
=
targetObject;
myType
=
targetObject.GetType();
myMethod
=
targetMethod;
}
public
Object Invoke(Object[] args)
{
if
(myType
!=
null
&&
myMethod
!=
null
)
{
BindingFlags myBindingFlags
=
BindingFlags.InvokeMethod
|
BindingFlags.Public;
if
(myObject
!=
null
)
{
myBindingFlags
=
myBindingFlags
|
BindingFlags.Static;
}
else
{
myBindingFlags
=
myBindingFlags
|
BindingFlags.Instance;
}
return
myType.InvokeMember(myMethod, myBindingFlags,
null
, myObject, args);
}
else
{
throw
new
Exception(
"
Valid
"
);
}
}
}
public
class
MyMath
{
public
static
int
Pow(
int
x,
int
y)
{
return
x
^
y;
}
public
int
Multiply(
int
x,
int
y)
{
return
x
*
y;
}
static
void
Main()
{
try
{
Object result;
Object[] args
=
{
2
,
3
};
MyMath objMath
=
new
MyMath();
Invoker myDelegate
=
new
Invoker(objMath,
"
Multiply
"
);
result
=
myDelegate.Invoke(args);
Console.WriteLine(
"
The product of {0} and {1} is: {2}
"
, args[
0
], args[
1
], result);
Invoker objDelegate
=
new
Invoker(
typeof
(MyMath),
"
Pow
"
);
result
=
objDelegate.Invoke(args);
Console.WriteLine(
"
The product of {0} and {1} is: {2}
"
, args[
0
], args[
1
], result);
}
catch
{
throw
;
}
}
}
在模拟中,委托可以使用委托链实现"反射"重载方法。