代码改变世界

反射学习笔记三(转载)

2010-02-24 15:48  爱研究源码的javaer  阅读(211)  评论(0编辑  收藏  举报

namespace Demo {

    public abstract class BaseClass {
      
    }

    public struct DemoStruct { }

    public delegate void DemoDelegate(Object sender, EventArgs e);

    public enum DemoEnum {
       terrible, bad, common=4, good, wonderful=8
    }

    public interface IDemoInterface {
       void SayGreeting(string name);     
    }

    public interface IDemoInterface2 {}
   
    public sealed class DemoClass:BaseClass, IDemoInterface,IDemoInterface2 {

       private string name;
       public string city;
       public readonly string title;
       public const string text = "Const Field";
       public event DemoDelegate myEvent;     
             
       public string Name {
           private get { return name; }
           set { name = value; }
       }

       public DemoClass() {
           title = "Readonly Field";
       }

       public class NestedClass { }

       public void SayGreeting(string name) {
           Console.WriteLine("Morning :" + name);
       }
    }

}

现在我们在 SimpleExplore项目中写一个方法AssemblyExplor(),查看我们Demo项目生成的程序集Demo.dll定义的全部类型:

public static void AssemblyExplore() {
    StringBuilder sb = new StringBuilder();

    Assembly asm = Assembly.Load("Demo");

    sb.Append("FullName(全名):" + asm.FullName + "\n");
    sb.Append("Location(路径):" + asm.Location + "\n");

    Type[] types = asm.GetTypes();

    foreach (Type t in types) {
       sb.Append("   类型:" + t + "\n");
    }

    Console.WriteLine(sb.ToString());
}

然后,我们在Main()方法中调用一下,应该可以看到这样的输出结果:

FullName(全名):Demo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Location(路径):E:\MyApp\TypeExplorer\SimpleExplorer\bin\Debug\Demo.dll
模块: Demo.dll
   类型:Demo.BaseClass
   类型:Demo.DemoStruct
   类型:Demo.DemoDelegate
   类型:Demo.DemoEnum
   类型:Demo.IDemoInterface
   类型:Demo.IDemoInterface2
   类型:Demo.DemoClass
   类型:Demo.DemoClass+NestedClass

反射基本类型

这里说反射基本类型,基本类型是针对 泛型类型 来说的,因为 反射泛型 会更加复杂一些。在前面的范例中,我们获得了程序集中的所有类型,并循环打印了它们,打印结果仅仅显示出了类型的全名,而我们通常需要关于类型更详细的信息,本节我们就来看看如何进一步查看类型信息。

NOTE:因为一个程序集包含很多类型,一个类型包含很多成员(方法、属性等),一个成员又包含很多其他的信息,所以如果我们从程序集层次开始写代码去获取每个层级的信息,那么会嵌套很多的foreach语句,为了阅读方便,我会去掉最外层的循环。

1.获取基本信息

有了前面Type一节的介绍,我想完成这里应该只是打打字而已,所以我直接写出代码,如有必要,会在注释中加以说明。我们再写一个方法TypeExplore,用于获取类型的详细信息(记得AssemblyExplore只获取了类型的名称):

public static void TypeExplore(Type t) {
    StringBuilder sb = new StringBuilder();

    sb.Append("名称信息:\n");
    sb.Append("Name: " + t.Name + "\n");
    sb.Append("FullName: " + t.FullName + "\n");
    sb.Append("Namespace: " + t.Namespace + "\n");

    sb.Append("\n其他信息:\n");
    sb.Append("BaseType(基类型): " + t.BaseType + "\n");
    sb.Append("UnderlyingSystemType: " + t.UnderlyingSystemType + "\n");

    sb.Append("\n类型信息:\n");
    sb.Append("Attributes(TypeAttributes位标记): " + t.Attributes + "\n");
    sb.Append("IsValueType(值类型): " + t.IsValueType + "\n");
    sb.Append("IsEnum(枚举): " + t.IsEnum + "\n");
    sb.Append("IsClass(类): " + t.IsClass + "\n");
    sb.Append("IsArray(数组): " + t.IsArray + "\n");
    sb.Append("IsInterface(接口): " + t.IsInterface + "\n");
    sb.Append("IsPointer(指针): " + t.IsPointer + "\n");
    sb.Append("IsSealed(密封): " + t.IsSealed + "\n");
    sb.Append("IsPrimitive(基类型): " + t.IsPrimitive + "\n");
    sb.Append("IsAbstract(抽象): " + t.IsAbstract + "\n");
    sb.Append("IsPublic(公开): " + t.IsPublic + "\n");
    sb.Append("IsNotPublic(不公开): " + t.IsNotPublic + "\n");
    sb.Append("IsVisible: " + t.IsVisible + "\n");
    sb.Append("IsByRef(由引用传递): " + t.IsByRef + "\n");

    Console.WriteLine(sb.ToString());
}

然后,我们在Main方法中输入:

Type t = typeof(DemoClass);
TypeExplore(t);

会得到这样的输出:

名称信息:
Name: DemoClass
FullName: Demo.DemoClass
Namespace: Demo

其他信息:
BaseType(基类型): Demo.BaseClass
UnderlyingSystemType: Demo.DemoClass

类型信息:
Attributes(TypeAttributes位标记): AutoLayout, AnsiClass, Class, Public, Sealed,
BeforeFieldInit
IsValueType(值类型): False
IsEnum(枚举): False
IsClass(类): True
IsArray(数组): False
IsInterface(接口): False
IsPointer(指针): False
IsSealed(密封): True
IsPrimitive(基类型): False
IsAbstract(抽象): False
IsPublic(公开): True
IsNotPublic(不公开): False
IsVisible: True
IsByRef(由引用传递): False

值得注意的是Attributes属性,它返回一个TypeAttributes位标记,这个标记标识了类型的一些元信息,可以看到我们熟悉的Class、Public、Sealed。相应的,IsClass、IsSealed、IsPublic等属性也返回为True。

2.成员信息 与 MemberInfo 类型

我们先考虑一下对于一个类型Type,可能会包含什么类型,常见的有字段、属性、方法、构造函数、接口、嵌套类型等。MemberInfo 类代表着 Type的成员类型,值得注意的是Type类本身又继承自MemberInfo类,理解起来并不困难,因为一个类型经常也是另一类型的成员。Type类提供 GetMembers()、GetMember()、FindMember()等方法用于获取某个成员类型。

我们再添加一个方法 MemberExplore(),来查看一个类型的所有成员类型。

public static void MemberExplore(Type t) {
    StringBuilder sb = new StringBuilder();

    MemberInfo[] memberInfo = t.GetMembers();

    sb.Append("查看类型 " + t.Name + "的成员信息:\n");

    foreach (MemberInfo mi in memberInfo) {
       sb.Append("成员:" + mi.ToString().PadRight(40) + " 类型: " + mi.MemberType + "\n");
    }

    Console.WriteLine(sb.ToString());
}

然后我们在Main方法中调用一下。

MemberExplore(typeof(DemoClass));

产生的输出如下:

查看类型 DemoClass的成员信息:
--------------------------------------------------
成员:Void add_myEvent(Demo.DemoDelegate)      类型: Method
成员:Void remove_myEvent(Demo.DemoDelegate)   类型: Method
成员:System.String get_Name()                 类型: Method
成员:Void set_Name(System.String)             类型: Method
成员:Void SayGreeting(System.String)          类型: Method
成员:System.Type GetType()                    类型: Method
成员:System.String ToString()                 类型: Method
成员:Boolean Equals(System.Object)            类型: Method
成员:Int32 GetHashCode()                      类型: Method
成员:Void .ctor()                             类型: Constructor
成员:System.String Name                       类型: Property
成员:Demo.DemoDelegate myEvent                类型: Event
成员:System.String text                       类型: Field
成员:Demo.DemoClass+NestedClass               类型: NestedType

我们使用了GetMembers()方法获取了成员信息的一个数组,然后遍历了数组,打印了成员的名称和类型。如同我们所知道的:Name属性在编译后成为了get_Name()和set_Name()两个独立的方法;myEvent事件的注册(+=)和取消注册(-=)分别成为了add_myEvent()和remove_myEvent方法。同时,我们发现私有(private)字段name 没有被打印出来,另外,基类System.Object的成员GetType()和Equals()也被打印了出来。

有的时候,我们可能不希望查看基类的成员,也可能希望查看私有的成员,此时可以使用GetMembers()的重载方法,传入BindingFlags 位标记参数来完成。BindingFlags位标记对如何获取成员的方式进行控制(也可以控制如何创建对象实例,后面会说明)。对于本例,如果我们想获取所有的公有、私有、静态、实例 成员,那么只需要这样修改GetMembers()方法就可以了。

MemberInfo[] memberInfo = t.GetMembers(
    BindingFlags.Public |
    BindingFlags.Static |
    BindingFlags.NonPublic |
    BindingFlags.Instance |
    BindingFlags.DeclaredOnly
);

此时的输出如下:

查看类型 DemoClass的成员信息:
--------------------------------------------------
成员:Void add_myEvent(Demo.DemoDelegate)      类型: Method
成员:Void remove_myEvent(Demo.DemoDelegate)   类型: Method
成员:System.String get_Name()                 类型: Method
成员:Void set_Name(System.String)             类型: Method
成员:Void SayGreeting(System.String)          类型: Method
成员:Void .ctor()                             类型: Constructor
成员:System.String Name                       类型: Property
成员:Demo.DemoDelegate myEvent                类型: Event
成员:System.String name                       类型: Field
成员:Demo.DemoDelegate myEvent                类型: Field
成员:System.String text                       类型: Field
成员:Demo.DemoClass+NestedClass               类型: NestedType

可以看到,继承自基类 System.Object 的方法都被过滤掉了,同时,打印出了私有的 name, myEvent 等字段。

现在如果我们想要获取所有的方法(Method),那么我们可以使用 Type类的FindMembers()方法:

MemberInfo[] memberInfo = t.FindMembers(
    MemberTypes.Method,      // 说明查找的成员类型为 Method
    BindingFlags.Public |
    BindingFlags.Static |
    BindingFlags.NonPublic |
    BindingFlags.Instance |
    BindingFlags.DeclaredOnly,
    Type.FilterName,
    "*"
);

Type.FilterName 返回一个MemberFilter类型的委托,它说明按照方法名称进行过滤,最后一个参数“*”,说明返回所有名称(如果使用“Get*”,则会返回所有以Get开头的方法)。现在的输出如下:

查看类型 DemoClass的成员信息:
--------------------------------------------------
成员:Void add_myEvent(Demo.DemoDelegate)      类型: Method
成员:Void remove_myEvent(Demo.DemoDelegate)   类型: Method
成员:System.String get_Name()                 类型: Method
成员:Void set_Name(System.String)             类型: Method
成员:Void SayGreeting(System.String)          类型: Method

MemberInfo 类有两个属性值得注意,一个是DeclaringType,一个是 ReflectedType,返回的都是Type类型。DeclaredType 返回的是声明该成员的类型。比如说,回顾我们之前的一段代码:

MemberInfo[] members = typeof(DemoClass).GetMembers();

它将返回所有的公有成员,包括继承自基类的Equals()等方法,对于Equals()方法来说,它的 DeclaringType 返回的是相当于 typeof(Object) 的类型实例,因为它是在 System.Object中被定义的;而它的ReflectedType 返回的则是相当于 typeof(DemoClass) 类型实例,因为它是通过 DemoClass 的类型实例被获取的。