代码改变世界

C#中反射的应用小结

2009-02-07 10:50  宝宝合凤凰  阅读(377)  评论(0编辑  收藏  举报

C#中反射的应用小结

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
{
    
/**//// <summary>
    
/// 被反射的类
    
/// </summary>
    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;}
        }
    }
    
/**//// <summary>
    
/// 自定义的过滤对象类
    
/// </summary>
    public class filterObject
    {
        
public string criterion1 = "Name";
        
public string criterion2 = "ID";
    }
    
public class Basics
    {
        
/**//// <summary>
        
/// 自定义的搜索条件,回调的方法
        
/// </summary>
        
/// <param name="memberInfo"></param>
        
/// <param name="filterCriteria"></param>
        
/// <returns></returns>
        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);
现在已经成功地创建了某个对象的一个实例,找到了该对象的某个方法,并且成功调用了此方法,而这些在设计之初没有必要知道该对象。

可以很容易的沿着这个例子向外延伸,创建一个类似于测试工具的实用工具。

posted on 2007-10-23 09:41 ZY.Zhou 阅读(500) 评论(2)  编辑 收藏 网摘

评论

#1楼 [楼主] 2008-01-22 09:19 ZY.Zhou      

补充说明:
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); //执行方法   回复  引用  查看    

#2楼  2008-12-14 15:58 朝阳      

很好,转载了!   回复  引用  查看