1.何谓反射?
反射就是在运行的时候发现对象的相关信息。根据这些信息可以动态的执行对象的方法以及获取对象的属性所储存的值。使用.NET Framework编 写的代码是自动反射的,或者说是自我描述的。之所以可以反射,是通过编译后产生的元数据来做到的。因此,你可以在你的程序中使用反射来查找托管代码中的类 型(包括类的名称,方法以及参数)和与其相关的信息这其中包括执行被发现的代码。你也可以在程序运行的时候使用反射来创建,编译和运行代码。
2.反射的实例化
l public static Assembly Load(string);
l public static Assembly LoadFrom(string);
例子:
(1)
Assembly myAssembly
=
Assembly.Load(
"
System.Drawing
"
);
(2)
string
path
=
@"
C:\WINNT\Microsoft.NET\Framework\v1.1.4322\System.Drawing.dll
"
;
Assembly myAssembly
=
Assembly.LoadFrom(path);
(3)
Assembly otherAssembly
=
typeof
(System.Data.DataRow).Assembly;
(4)
DataTable dt
=
new
DataTable();
Assembly otherAssembly
=
dt.GetType().Assembly;
/*先定义一个CLASS,以方便以下例子使用*/
using
System;
using
System.Reflection;
namespace
Basics
{
public
enum
testEnum
{
testValue
=
1
}
public
class
testClass
{
public
static
void
Main()
{
}
}
public
class
SomeClass
{
public
SomeClass()
{}
public
SomeClass(
int
someValue)
{}
public
SomeClass(
int
someValue,
int
someOtherValue)
{}
public
void
SomeMethod()
{}
}
public
class
OtherClass
{
public
void
OtherMethod()
{}
public
static
void
OtherStaticMethod()
{}
public
static
void
AnotherStaticMethod()
{}
}
public class AnotherClass
{
private int myPrvField1 = 15;
private string myPrvField2 = "Some private field";
public decimal myPubField1 = 1.03m;
}
}
第一部分:找到特定的成员
3.列出程序集中类的名称
Assembly myAssembly
=
Assembly.Load(
"
Basics
"
);
Type[] types
=
myAssembly.GetTypes();
foreach
(Type type
in
types)
{
if
(type.IsClass)
Console.WriteLine(type.Name);
}
4.直接访问某个类(假如访问的是SomeClass类中带一个参数的方法)
Type[] ts
=
{typeof(Int32)}
;
ConstructorInfo ci
=
typeof
(SomeClass).GetConstructor(ts);
或者
MethodInfo mi
=
typeof
(SomeClass).GetMethod(
"
SomeMethod
"
);
5.过滤某些特定的成员
System.Type类也提供了一些方法,用于把包含在一个类里或者其它的类型里的特定的类型过滤到一个集合中。如GetConstructors方法,GetMethods方法,GetProperties方法和GetEvents方法均允许你以数组的方式返回所有给定的类型或者通过使用过滤条件只返回特定的类型集合。
以下是过滤出是公用和静态的方法
MethodInfo[] mis
=
typeof
(OtherClass).GetMethods(BindingFlags.Public
|
BindingFlags.Static);
PS:如果想得到私有类型,则用BindingFlags.NonPublic,但你需要有相应的权限
6.搜索某些特定的成员
FieldInfo fi;
AnotherClass ac
=
new
AnotherClass();
MemberInfo[] memInfo
=
ac.GetType().FindMembers(MemberTypes.Field,BindingFlags.NonPublic
|
BindingFlags.Instance,
null
,
null
);
foreach
(MemberInfo m
in
memInfo)
{
fi
=
m
as
FieldInfo;
if
(fi
!=
null
)
{
Console.WriteLine(
"
{0} of value:{1}
"
,fi.Name,fi.GetValue(ac));
}
}
7.自定义搜索
这个程序定义了一个委托(与委托MemberFilter具有相同签名的方法)MySearchDelegate,用于定制搜索条件。创建了一个类filterObject,其包含两个字段,辅助我们自定义搜索条件。程序中调用了FindMembers,指出我们需要所有的属性类型。当一个属性类型被发现时,程序将激发MySearchDelegate并且传给它一个filterCriteria实例对象,这个委托将判断成员的名称是否满足自定义的搜索条件来返回True还是False。
using
System;
using
System.Reflection;
namespace
Basics2
{
/**/
///
///
被反射的类
///
public
class
SomeClass
{
private
int
m_id;
public
int
ID
{
get
{
return
this
.m_id;}
set
{
this
.m_id
=
value;}
}
private
string
m_name;
public
string
Name
{
get
{
return
this
.m_name;}
set
{
this
.m_name
=
value;}
}
private
int
m_type;
public
int
Type
{
get
{
return
this
.m_type;}
set
{
this
.m_type
=
value;}
}
}
/**/
///
///
自定义的过滤对象类
///
public
class
filterObject
{
public
string
criterion1
=
"
Name
"
;
public
string
criterion2
=
"
ID
"
;
}
public
class
Basics
{
/**/
///
///
自定义的搜索条件,回调的方法
///
///
///
///
public
static
bool
MySearchDelegate(MemberInfo memberInfo,
object
filterCriteria)
{
if
(memberInfo.Name
==
((filterObject)filterCriteria).criterion1
||
memberInfo.Name
==
((filterObject)filterCriteria).criterion2)
return
true
;
return
false
;
}
public
static
void
Main()
{
PropertyInfo pi;
//
绑定自定义的搜索条件
MemberFilter mf
=
new
MemberFilter(MySearchDelegate);
SomeClass sc
=
new
SomeClass();
//
使用FindMembers返回指定的属性
MemberInfo[] memInfo
=
sc.GetType().FindMembers(MemberTypes.Property,BindingFlags.GetProperty
|
BindingFlags.Public
|
BindingFlags.Instance,mf,
new
filterObject());
foreach
(MemberInfo m
in
memInfo)
{
pi
=
m
as
PropertyInfo;
Console.WriteLine(pi.Name);
}
Console.ReadLine();
}
}
}
第二部分 执行发现的代码
执行发现的代码的过程基本上要遵循以下几个基本的步骤:
l 加载程序集
l 找到你希望使用的类型或者类
l 创建该类型(或者类)的一个实例
l 找到你希望执行的该类型的某个方法
l 得到该方法的参数
l 调用该对象上的方法并且传递给它恰当的参数
8.创建发现的代码的实例
一旦找到你要找的类型,就可以使用System.Activator创建此类型的一个实例。你将会使用Activator类的方法CreateInstance众多版本中的一个。CreateInstance允许你指定你想要创建的对象,并且可选择的参数会应用到该对象的构造器上。
(1)在这里该对象的默认的构造器不用传递参数:
//
SomeClass为一个类
object
obj
=
System.Activator.CreateInstance(
typeof
(SomeClass));
(2)假设你想创建一个特定对象的实例,其构造器是需要传递参数的。为此你需要把这些参数的值作为一个数组的形式传递给CreateInstance。每一个参数的值需要对应该参数的类型,并且数组中参数的值需要与构造器的签名的顺序相一致。
Type[] ts
=
{
typeof
(Int32)};
ConstructorInfo ci
=
typeof
(SomeClass).GetConstructor(ts);
ParameterInfo[] pi
=
ci.GetParameters();
//
创建一个数组,与返回的长度一样
object
[] param
=
new
object
[pi.Length];
//
给数组每个参数赋值
foreach
(ParameterInfo p
in
pi)
{
if
(p.ParameterType
==
typeof
(Int32))
param[p.Position]
=
100
;
//
赋值
}
//
最后可以创建实例化了
object
o
=
System.Activator.CreateInstance(
typeof
(SomeClass),param);
到此,你已经得到了你的对象(SomeClass)的一个实例(o)。接下来,让我们了解一下,如何调用该对象的方法。在之前,我们查询构造器的参数并把参数的值传给构造器,对于方法而言,这个处理过程是一样的。假设SomaClass类有一个SomeMethod方法,你想调用这个方法。为了保证例子足够简单,假设方法SomeMethod没有任何参数(参数的处理过程同上)。为了能够调用SomeMethod,你需要获取关于该方法的MethodInfo对象的一个引用。在这里你可以使用GetMethod或者GetMethods方法在你的类型上搜索。让我们使用GetMethod,并给其传递方法的名称。
MethodInfo mi
=
typeof
(SomeClass).GetMethod(
"
SomeMethod
"
);
你不仅拥有了SomeClass的一个实例,而且也拥有了你希望调用该对象方法的引用mi,因此你可以使用MethodInfo.Invoke调用你的目标方法了。你需要传递包含该方法的对象的实例和该方法需要的一组参数的值。如下所示:
mi.Invoke(o,
null
);
现在已经成功地创建了某个对象的一个实例,找到了该对象的某个方法,并且成功调用了此方法,而这些在设计之初没有必要知道该对象。
可以很容易的沿着这个例子向外延伸,创建一个类似于测试工具的实用工具。
补充:
补充说明:
1.Assembly myAssembly = Assembly.LoadFrom("TestClass.dll");
Type[] myType = myAssembly.GetTypes();
以上myType中的每个元素实际上是指dll中的每个CLASS类.如果要控制第一个类则用myType[0]来表示.
2. MethodInfo[] mi = myType[0].GetMethods();
foreach(MethodInfo m in mi)
{
// this.textBox1.Text += m.Name + "\r\n";
}
以上代码获得DLL第一个类中的所有方法
3. PropertyInfo[] pi = myType[0].GetProperties();
foreach(PropertyInfo p in pi)
{
// this.textBox1.Text += p.Name + "\r\n";
}
以上代码获得DLL中第一个类的所有属性
4. 其实用MemberInfo[] 可以获得以上所有属性及方法
5. 执行DLL中的方法前需要实例化构造方法获得入口实例.
Type[] ts = {typeof(String),typeof(Int32)};//构造方法的参数,如果没有可以省略
ConstructorInfo ci = myType[0].GetConstructor(ts);//在重载中找到相应的构造函数
ParameterInfo[] pi = ci.GetParameters();//取得构造函数的参数信息
object[] paras = new object[pi.Length];
paras[0] = "ZZY";//给参数赋值,假设有两个参数,一个为String,一个为Int
paras[1] = 10;
object o = System.Activator.CreateInstance(myType[0],paras);//创建实例
// this.textBox1.Text += o.ToString() + "\r\n";
MethodInfo mi = myType[0].GetMethod("WriteFile");//找到要执行的方法
ParameterInfo[] pi1 = mi.GetParameters();//获得方法的参数信息
object[] paras1 = new object[pi1.Length];
paras1[0] = "test.txt";//给参数赋值,假设有两个参数都为String
paras1[1] = "Pls have your test";
mi.Invoke(o,paras1); //执行方法