在获取一个Type之后,这个类型的成员可能包含字段,构造器,方法,属性,事件和嵌套类型。接下来就看说下如何查询一个类型的成员。

   1.发现类型的成员

   上一章提到了System.Reflection.MemberInfo类型,这是一个抽象基类,而我们的类型成员是从MemberInfo派生的一组类。具体的层次结构如下。

   

      可以调用GetMembers方法,传入BindingFlags参数,然后返回由MemberInfo派生对象构成的一个数组。

 1   Assembly assemblyFromPath = Assembly.LoadFile(@"E:\StrongNameDLL.dll");
 2   const BindingFlags bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
 3   foreach (Type t in assemblyFromPath.GetExportedTypes())
 4   {
 5       foreach (MemberInfo mi in t.GetMembers(bf))
 6       {
 7           if (mi is FieldInfo)
 8           {
 9               //...
10           }
11           if (mi is MethodInfo)
12           {
13               //...
14           }
15       }
16   }

        不仅如此,Type还提供了一些方法能够返回特定的成员类型,例如GetNestedTypes,GetFields, GetConstructors, GetMethods, GetProperties, GetEvents,返回的都是一个Info对象的数组;以及对应的GetNestedType,GetField,GetConstructor,GetMethod,GetProperty,GetEvent单数形式方法,返回Info对象。

    2.调用类型的成员

    Type类提供了一个InvokeMember方法,可通过它调用一个成员。  

1 public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, CultureInfo culture)
2 {
3     //...
4 }

        在内部,InvokeMember会执行两个操作。首先,它必须绑定要调用的成员;其次,必须调用成员。

    a.name参数传递一个String,指出希望绑定的成员的名称,而除了target参数,其他参数都是用于帮助InvokeMember方法确定要绑定的成员。

        b.BindingFlags标识了筛选种类以及具体操作。

        c.Binder封装了选在一个成员时的规则,例如BindToField, BindToMethod等。如果传入null,内部使用DefaultBinder对象。

        d.target参数是对想要调用其成员的一个对象的引用。如果要调用一个类型的静态成员,该参数应传递null值。

        e.args表示要传给方法的实参


        3.代码实现

    代码实现了几种不同的访问类型成员的方式,目的是希望大家体会到一次绑定,多次调用的好处。  

  1     class Program
  2     {
  3         private const BindingFlags c_bf = BindingFlags.DeclaredOnly | BindingFlags.Public| BindingFlags.NonPublic
  4             | BindingFlags.Instance;
  5 
  6         static void Main(string[] args)
  7         {
  8             Type t = typeof(SomeType);
  9             //利用Type的InvokeMember来绑定并调用一个成员
 10             UseInvokeMemberToBindAndInvokeTheMember(t);
 11             Console.WriteLine();
 12             //绑定成员,并在以后调用它
 13             BindToMemberThenInvokeTheMember(t);
 14             Console.WriteLine();
 15             //绑定一个对象或成员,然后创建委托来调用,速度快
 16             BindToMemberCreateDelegateToMemberThenInvokeTheMember(t);
 17             Console.WriteLine();
 18             //用dynamic基元类简化访问成员时的语法
 19             UseDynamicToBindAndInvokeTheMember(t);
 20             Console.WriteLine();
 21         }
 22 
 23         private static void UseInvokeMemberToBindAndInvokeTheMember(Type t)
 24         {
 25             Console.WriteLine("UseInvokeMemberToBindAndInvokeTheMember");
 26             //构造实例
 27             object[] args = new object[] { 12 };
 28             object obj = t.InvokeMember(null, c_bf | BindingFlags.CreateInstance, null, null, args);
 29             Console.WriteLine("Type:{0}, Value:{1}.", obj.GetType().ToString(), args[0]);
 30 
 31             //读写一个字段
 32             t.InvokeMember("m_someField", c_bf | BindingFlags.SetField, null, obj, new object[] { 5 });
 33             int field = (int)t.InvokeMember("m_someField", c_bf | BindingFlags.GetField, null, obj, null);
 34             Console.WriteLine("SomeField: " + field);
 35 
 36             //调用一个方法
 37             string toStringMethod = (string)t.InvokeMember("ToString", c_bf | BindingFlags.InvokeMethod, null, obj, null);
 38             Console.WriteLine("ToString: " + toStringMethod);
 39 
 40             //读写一个属性
 41             try
 42             {
 43                 t.InvokeMember("SomeProp", c_bf | BindingFlags.SetProperty, null, obj, new object[] { 0 });
 44             }
 45             catch (TargetInvocationException e)
 46             {
 47                 if (e.InnerException.GetType() != typeof(ArgumentOutOfRangeException)) throw;
 48                 Console.WriteLine("Property set catch");
 49             }
 50 
 51             t.InvokeMember("SomeProp", c_bf | BindingFlags.SetProperty, null, obj, new object[] { 2 });
 52             int property = (int)t.InvokeMember("SomeProp", c_bf | BindingFlags.GetProperty, null, obj, null);
 53             Console.WriteLine("SomeProp: " + property);
 54 
 55             //调用事件add/remove方法,为事件添加和删除一个委托
 56             EventHandler eh = new EventHandler(EventCallBack);
 57             t.InvokeMember("add_SomeEvent", c_bf | BindingFlags.InvokeMethod, null, obj, new object[] { eh });
 58             t.InvokeMember("remove_SomeEvent", c_bf | BindingFlags.InvokeMethod, null, obj, new object[] { eh });
 59         }
 60 
 61         private static void BindToMemberThenInvokeTheMember(Type t)
 62         {
 63             //构造一个实例
 64             ConstructorInfo ctor = t.GetConstructor(new Type[] { typeof(Int32).MakeByRefType() });
 65             //ConstructorInfo ctor = t.GetConstructor(new Type[] { Type.GetType("System.Int32&") });
 66             object[] args = new object[] { 12 };
 67             object obj = ctor.Invoke(args);
 68 
 69             //读写一个字段
 70             FieldInfo fi = obj.GetType().GetField("m_someField", c_bf);
 71             fi.SetValue(obj, 33);
 72             Console.WriteLine("SomeField: " + fi.GetValue(obj));
 73 
 74             //调用一个方法
 75             MethodInfo mi = obj.GetType().GetMethod("ToString", c_bf);
 76             string toString = (string)mi.Invoke(obj, null);
 77             Console.WriteLine("ToString: " + toString);
 78 
 79             //读写一个属性
 80             PropertyInfo pi = obj.GetType().GetProperty("SomeProp", typeof(Int32));
 81             try
 82             {
 83                 pi.SetValue(obj, 0, null);
 84             }
 85             catch (TargetInvocationException e)
 86             {
 87                 if (e.InnerException.GetType() != typeof(ArgumentOutOfRangeException)) throw;
 88                 Console.WriteLine("Property set catch");
 89             }
 90             pi.SetValue(obj, 2, null);
 91             Console.WriteLine("SomeProp: " + pi.GetValue(obj,null));
 92 
 93             //为事件添加删除一个委托
 94             EventInfo ei = obj.GetType().GetEvent("SomeEvent", c_bf);
 95             EventHandler eh = new EventHandler(EventCallBack);
 96             ei.AddEventHandler(obj,eh);
 97             ei.RemoveEventHandler(obj,eh);
 98         }
 99 
100         private static void BindToMemberCreateDelegateToMemberThenInvokeTheMember(Type t)
101         {
102             //构造一个实例
103             object[] args = new object[] { 12 };
104             object obj = Activator.CreateInstance(t, args);
105 
106             //不能创建对一个字段的委托
107 
108             //调用一个方法
109             MethodInfo mi = obj.GetType().GetMethod("ToString", c_bf);
110             var toString = (Func<string>)Delegate.CreateDelegate(typeof(Func<string>), obj, mi);
111             string s = toString();
112             Console.WriteLine("ToString: " + s);
113 
114             //读写一个属性
115             PropertyInfo pi = obj.GetType().GetProperty("SomeProp", c_bf);
116             var setSomeProp = (Action<int>)Delegate.CreateDelegate(typeof(Action<int>), obj, pi.GetSetMethod());
117             try
118             {
119                 setSomeProp(0);
120             }
121             catch (ArgumentOutOfRangeException e)
122             {
123                 Console.WriteLine("Property set catch");
124             }
125             setSomeProp(2);
126             var getSomeProp = (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), obj, pi.GetGetMethod());
127             Console.WriteLine("SomeProp: " + getSomeProp);
128 
129             //从事件中添加删除一个委托
130             EventInfo ei = obj.GetType().GetEvent("SomeEvent", c_bf);
131             var addSomeEvent = (Action<EventHandler>)Delegate.CreateDelegate(typeof(Action<EventHandler>), obj, ei.GetAddMethod());
132             addSomeEvent(EventCallBack);
133             var removeSomeEvent = (Action<EventHandler>)Delegate.CreateDelegate(typeof(Action<EventHandler>), obj, ei.GetRemoveMethod());
134             removeSomeEvent(EventCallBack);
135         }
136 
137         private static void UseDynamicToBindAndInvokeTheMember(Type t)
138         {
139             //构造一个实例
140             object[] args = new object[] { 12 };
141             dynamic obj = Activator.CreateInstance(t, args);
142 
143             try
144             {
145                 obj.m_someField = 5;
146                 int v = (int)obj.m_someField;
147                 Console.WriteLine("someField: " + v);
148             }
149             catch (RuntimeBinderException e)
150             {
151                 //字段私有
152                 Console.WriteLine("Failed to access field: " + e.Message);
153             }
154 
155             //调用一个方法
156             string toString = (string)obj.ToString();
157             Console.WriteLine("ToString: " + toString);
158 
159             //读写一个属性
160             try
161             {
162                 obj.SomeProp = 0;
163             }
164             catch (ArgumentOutOfRangeException e)
165             {
166                 Console.WriteLine("Property set catch");
167             }
168             obj.SomeProp = 2;
169             int value = (int)obj.SomeProp;
170             Console.WriteLine("SomeProp: " + value);
171 
172             //添加删除一个委托
173             obj.SomeEvent += new EventHandler(EventCallBack);
174             obj.SomeEvent -= new EventHandler(EventCallBack);
175         }
176 
177         private static void EventCallBack(object sender, EventArgs e){ }
178     }
179 
180     internal sealed class SomeType
181     {
182         private int m_someField;
183         public SomeType(ref int x)
184         {
185             x *= 2;
186         }
187         public override string ToString()
188         {
189             return m_someField.ToString();
190         }
191         public int SomeProp
192         {
193             get { return m_someField; }
194             set
195             {
196                 if (value < 1)
197                 {
198                     throw new ArgumentOutOfRangeException("value");
199                 }
200                 m_someField = value;
201             }
202         }
203         public event EventHandler SomeEvent;
204         private void NonCompilerWarning()
205         {
206             SomeEvent.ToString();
207         }
208     }

 

     4.使用绑定句柄减少内存耗用

     应用程序绑定一组类型或者类型成员到一个集合中,然后在某个时刻,应用程序会搜索这个集合,查找特定的对象,然后调用这个对象。这是个很好机制,只是有一个小问题:Type对象和MemberInfo派生对象需要大量的内存。如果应用程序容纳太多这样的对象,并且只是偶尔调用它们,那么内存耗用会很大,并对程序的性能产生影响。

     我们可以使用运行时句柄(runtime handles)来替代这些对象,从而减小占用的内存。FCL定义了3个运行时句柄类型:RuntimeTypeHandle, RuntimeFieldHandle和RuntimeMethodHandle。它们都是值类型,只包含一个IntPrt。Type或MemberInfo对象与运行时句柄之间的转换相当简单。下面通过具体代码演示。

 1     internal sealed class ExchangedDemo
 2     {
 3         private const BindingFlags c_bf = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic |
 4              BindingFlags.Instance | BindingFlags.Static;
 5 
 6         public static void ShowExchangeMethod()
 7         {
 8             Show("Before doing anything.");
 9             List<MethodBase> methodInfos = new List<MethodBase>();
10             foreach (Type t in typeof(Object).Assembly.GetExportedTypes())
11             {
12                 if (t.IsGenericTypeDefinition)
13                 {
14                     continue;
15                 }
16 
17                 MethodBase[] mBase = t.GetMethods(c_bf);
18                 methodInfos.AddRange(mBase);
19             }
20 
21             Console.WriteLine("# of methods={0:###,###}",methodInfos.Count);
22             Show("After building cache of MethodInfo objects");
23 
24             List<RuntimeMethodHandle> methodHandles = methodInfos.ConvertAll<RuntimeMethodHandle>(
25                 mBase => mBase.MethodHandle);
26             Show("Holding MethodInfo and RuntimeMethodHandl cache");
27             GC.KeepAlive(methodInfos);
28 
29             methodInfos = null;
30             Show("After freeing MethodInfo objects");
31 
32             methodInfos = methodHandles.ConvertAll<MethodBase>(rmh => MethodBase.GetMethodFromHandle(rmh));
33             Show("Size of heap after re-creating MethodInfo objects");
34 
35             GC.KeepAlive(methodHandles);
36             GC.KeepAlive(methodInfos);
37 
38             methodInfos = null;
39             methodHandles = null;
40             Show("After freeing MethodInfos and RuntimeMethodHandles");
41         }
42 
43         private static void Show(string msg)
44         {
45             Console.WriteLine("Heap size = {0,12:##,###,###} - {1}",GC.GetTotalMemory(true),msg);
46         }
47     }