在获取一个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 }