前言
C#有关反射的话题已经是个老生常谈的话题,也许园友一看这标题都不屑去看了,但是既然拿出来讲必有讲之道理,当然,不喜勿喷,高手请绕道!直入话题。
讨论
定义一个Person类代码如下
1 public class Person
2 {
3
4 /// <summary>
5 /// 年龄
6 /// </summary>
7 public int Age { get; set; }
8
9 /// <summary>
10 /// 姓名
11 /// </summary>
12 public string Name { get; set; }
13
14 /// <summary>
15 /// 性别
16 /// </summary>
17 public bool Gender { get; set; }
18
19
20 /// <summary>
21 /// 求两个数的和
22 /// </summary>
23 /// <param name="num1"></param>
24 /// <param name="num2"></param>
25 /// <returns></returns>
26 public int Add(int num1,int num2)
27 {
28 return num1 + num2;
29 }
30 }
那么现在怎么动态获取该对象并打印该对象?啊,用反射动态获取呗,ok,实现如下!
1 Type person = typeof(Person);
2
3 Person t = (Person)Activator.CreateInstance(person) as Person;
4
5 Console.WriteLine(t.ToString());
完全没错,在黑框框中运行输出入下:

接下来小小改动一下,在Person类中添加一个构造函数
1 public Person(string age, string name, bool gender)
2 {
3 this.Age = age;
4 this.Name = name;
5 this.Gender = gender;
6 }
此时我们再来运行看看!!什么鬼,怎么出现错误了???

吓我一跳,平常来个未将对象设置到对象的实例那是见怪不怪了,出现这个容我想想,无参构造函数似乎暗示着什么,突然醒悟对象不都默认有个无参的构造函数吗,啊,shit,原来是因为我定义了一个有参数的构造函数,用Activator.CreateInstance动态创建对象调用的无参构造函数啊,紧接着我将鼠标放在该方法跟前,都告诉我了写着 使用指定类型的默认构造函数来创建该类型的实例 ,知道错误所在了,关键是怎么去解决,要是类中写了有参数的构造函数,现在想要反射来动态创建对象岂不是不能够了吗?继续想,记得在javascript中虽然不能够像C#实现重载,当然js也不存在重载,但是可以根据arguments.length来近似于实现所谓的重载,这里同样可不可以根据构造函数的个数来实现呢?有了这个想法就开干,当看到这个GetConstructors方法心底就舒坦起来了,经过反复查看其方法改造控制台后的代码如下:
1 var length = 0;
2 Person p = null;
3 Type person = typeof(Person);
4 var gc = person.GetConstructors();
5 foreach (var c in gc)
6 {
7 length = c.GetParameters().Length;
8 }
现在获取到了构造函数的长度即可以根据参数的个数来进行创建对象,离解决问题更进一步了,这时我想到如果我参数个数相同,怎么知道我是调用哪个构造函数呢?对,根据参数的类型,所以现在问题上升到怎么确定我要传递参数的类型呢?看看构造函数的属性 ConstructorInfo 中有没有什么方法可以定义参数类型,皇天不负有心人 GetConstructor 方法参数中 有个Type 这就是参数的类型了,然后利用 Invoke 委托对构造函数传入参数获取对象,如下:
1 ConstructorInfo Constructor = null;
2
3 switch (length)
4 {
5 case 0:
6 Constructor = person.GetConstructor(new Type[0]);
7 p = Constructor.Invoke(null) as Person;
8 break;
9 case 1:
10 Constructor = person.GetConstructor(new Type[1] { typeof(int) });
11 p = Constructor.Invoke(new object[1] { 1 }) as Person;
12 break;
13 case 2:
14 Constructor = person.GetConstructor(new Type[2] { typeof(int), typeof(string) });
15 p = Constructor.Invoke(new object[2] { 1, "嘿嘿" }) as Person;
16 break;
17 case 3:
18 Constructor = person.GetConstructor(new Type[3] { typeof(int), typeof(string), typeof(bool) });
19 p = Constructor.Invoke(new object[3] { 1, "嘿嘿", false }) as Person;
20 break;
21 default:
22 break;
23 }
24
25 //Person t = (Person)Activator.CreateInstance(person) as Person;
26 Console.WriteLine(p.ToString());
同样得到上述结果打印出:反射之动态创建对象.Person,ok,终于解决了,完美!
拓展
在上述过程中用到委托Invoke再传入参数到其中,鉴于此对于反射,参考代码改善建议利用dynamic关键字简化反射实现。下面用例子说明,利用反射计算Person类中方法计算两个数的和。利用反射立马能够写出
1 Person dy = new Person();
2 var p= typeof(Person).GetMethod("Add");
3 Convert.ToInt32(p.Invoke(dy, new object[] { 30, 40 });)
如果利用 dynamic 关键字能够更加精简而且更加优美
1 dynamic dy = new Person();
2 dy.Add(30, 40);
总结
(1)利用反射动态创建对象两种方法
【1】利用Activator.CreateInstance,前提是调用对象的默认无参构造函数
【2】利用构造器来动态创建对象
(2)利用dynamic关键字来简化反射实现
补充1
用构造器将其进行封装为如下,其中用时需要手动添加参数类型以及参数默认值
1 public static T GetEntity<T>() where T : class
2 {
3 T entity = null;
4 var length = 0;
5 Type t = typeof(T);
6 var gc = t.GetConstructors();
7
8 foreach (var c in gc)
9 {
10 length = c.GetParameters().Length;
11 }
12 ConstructorInfo Constructor = null;
13
14 switch (length)
15 {
16 case 0:
17 Constructor = t.GetConstructor(new Type[0]);
18 entity = Constructor.Invoke(null) as T;
19 break;
20 case 1:
21
22 Constructor = t.GetConstructor(new Type[1] { typeof(int) });
23 entity = Constructor.Invoke(new object[1] { 0 }) as T;
24 break;
25 case 2:
26 Constructor = t.GetConstructor(new Type[2] { typeof(int), typeof(string) });
27 entity = Constructor.Invoke(new object[2] { 0, null }) as T;
28 break;
29 case 3:
30 Constructor = t.GetConstructor(new Type[3] { typeof(int), typeof(string), typeof(bool) });
31 entity = Constructor.Invoke(new object[3] { 0, null, false }) as T;
32 break;
33 default:
34 break;
35 }
36
37 return entity;
38 }
补充2
上述提到用 dynamic 来简化反射的实现,对于园友提出 对于反射无法获取到class是什么 ,像 dynamic dy = newPerson(); Person dy= new Person() ; 似乎是一样的,那还不如直接实例化调用其方法即可,一想确实是这样,经过再次研究觉得用dynamic只是更加便捷而且代码更加精简,就像用lamda简化而省去了用委托或者匿名方法一样!下面就以一个实例来说明不得不用反射来实现,还用上面的Person类,现在继续添加一个 OtherPerson 类:
1 public class OtherPerson
2 {
3 private int OtherAge { get; set; }
4 }
然后在Person类中添加一个返回值为OtherPerson的私有方法 GetOtherPerson
1 private OtherPerson GetOtherPerson()
2 {
3 OtherPerson op = new OtherPerson();
4 return op;
5 }
现在想调用 GetOtherPerson 方法获取 OtherPerson 类中的私有字段 OtherAge ,别告诉我直接实例化Person对象,再调用,因为是私有现在无法实现,所以马上想到的是通过反射来实现获取这个方法再同样实现获取私有字段
1 Person p1 = new Person();
2 var p = typeof(Person).InvokeMember("GetOtherPerson", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, p1, null);
3 var propInfo = p.GetType().GetProperty("OtherAge", BindingFlags.Instance | BindingFlags.NonPublic);
4 var age = (int)propInfo.GetValue(p, null);
一大片代码看起来是不是很恶心,接下来我们将代码进行改进,使其便捷化,上述提到用dynamic来实现,所以就来吧!
var age = ((dynamic)p1).GetOtherPerson().OtherAge; 就一行代码是不是很简单,再次说明了dynamic的优美和简洁,so perfect!那我们运行下看看吧,oh,往往在你最得意的时候结果就会给你当头一棒,出错了!如下

这保护的级别有点忒高,那必须攻破你的堡垒!弄了一下午最终还是google给出了一位前辈已经这么做过的解决方案!重写了dynamic的基类DynamicObject,接着就写了它的扩展方法,代码如下:

1 public class PrivateReflectionDynamicObject : DynamicObject
2 {
3
4 private static IDictionary<Type, IDictionary<string, IProperty>> _propertiesOnType = new ConcurrentDictionary<Type, IDictionary<string, IProperty>>();
5
6 // Simple abstraction to make field and property access consistent
7 interface IProperty
8 {
9 string Name { get; }
10 object GetValue(object obj, object[] index);
11 void SetValue(object obj, object val, object[] index);
12 }
13
14 // IProperty implementation over a PropertyInfo
15 class Property : IProperty
16 {
17 internal PropertyInfo PropertyInfo { get; set; }
18
19 string IProperty.Name
20 {
21 get
22 {
23 return PropertyInfo.Name;
24 }
25 }
26
27 object IProperty.GetValue(object obj, object[] index)
28 {
29 return PropertyInfo.GetValue(obj, index);
30 }
31
32 void IProperty.SetValue(object obj, object val, object[] index)
33 {
34 PropertyInfo.SetValue(obj, val, index);
35 }
36 }
37
38 // IProperty implementation over a FieldInfo
39 class Field : IProperty
40 {
41 internal FieldInfo FieldInfo { get; set; }
42
43 string IProperty.Name
44 {
45 get
46 {
47 return FieldInfo.Name;
48 }
49 }
50
51
52 object IProperty.GetValue(object obj, object[] index)
53 {
54 return FieldInfo.GetValue(obj);
55 }
56
57 void IProperty.SetValue(object obj, object val, object[] index)
58 {
59 FieldInfo.SetValue(obj, val);
60 }
61 }
62
63
64 private object RealObject { get; set; }
65 private const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
66
67 internal static object WrapObjectIfNeeded(object o)
68 {
69 // Don't wrap primitive types, which don't have many interesting internal APIs
70 if (o == null || o.GetType().IsPrimitive || o is string)
71 return o;
72
73 return new PrivateReflectionDynamicObject() { RealObject = o };
74 }
75
76 public override bool TryGetMember(GetMemberBinder binder, out object result)
77 {
78 IProperty prop = GetProperty(binder.Name);
79
80 // Get the property value
81 result = prop.GetValue(RealObject, index: null);
82
83 // Wrap the sub object if necessary. This allows nested anonymous objects to work.
84 result = WrapObjectIfNeeded(result);
85
86 return true;
87 }
88
89 public override bool TrySetMember(SetMemberBinder binder, object value)
90 {
91 IProperty prop = GetProperty(binder.Name);
92
93 // Set the property value
94 prop.SetValue(RealObject, value, index: null);
95
96 return true;
97 }
98
99 public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
100 {
101 // The indexed property is always named "Item" in C#
102 IProperty prop = GetIndexProperty();
103 result = prop.GetValue(RealObject, indexes);
104
105 // Wrap the sub object if necessary. This allows nested anonymous objects to work.
106 result = WrapObjectIfNeeded(result);
107
108 return true;
109 }
110
111 public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
112 {
113 // The indexed property is always named "Item" in C#
114 IProperty prop = GetIndexProperty();
115 prop.SetValue(RealObject, value, indexes);
116 return true;
117 }
118
119 // Called when a method is called
120 public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
121 {
122 result = InvokeMemberOnType(RealObject.GetType(), RealObject, binder.Name, args);
123
124 // Wrap the sub object if necessary. This allows nested anonymous objects to work.
125 result = WrapObjectIfNeeded(result);
126
127 return true;
128 }
129
130 public override bool TryConvert(ConvertBinder binder, out object result)
131 {
132 result = Convert.ChangeType(RealObject, binder.Type);
133 return true;
134 }
135
136 public override string ToString()
137 {
138 return RealObject.ToString();
139 }
140
141 private IProperty GetIndexProperty()
142 {
143 // The index property is always named "Item" in C#
144 return GetProperty("Item");
145 }
146
147 private IProperty GetProperty(string propertyName)
148 {
149
150 // Get the list of properties and fields for this type
151 IDictionary<string, IProperty> typeProperties = GetTypeProperties(RealObject.GetType());
152
153 // Look for the one we want
154 IProperty property;
155 if (typeProperties.TryGetValue(propertyName, out property))
156 {
157 return property;
158 }
159
160 // The property doesn't exist
161
162 // Get a list of supported properties and fields and show them as part of the exception message
163 // For fields, skip the auto property backing fields (which name start with <)
164 var propNames = typeProperties.Keys.Where(name => name[0] != '<').OrderBy(name => name);
165 throw new ArgumentException(
166 String.Format(
167 "The property {0} doesn't exist on type {1}. Supported properties are: {2}",
168 propertyName, RealObject.GetType(), String.Join(", ", propNames)));
169 }
170
171 private static IDictionary<string, IProperty> GetTypeProperties(Type type)
172 {
173 // First, check if we already have it cached
174 IDictionary<string, IProperty> typeProperties;
175 if (_propertiesOnType.TryGetValue(type, out typeProperties))
176 {
177 return typeProperties;
178 }
179
180 // Not cache, so we need to build it
181
182 typeProperties = new ConcurrentDictionary<string, IProperty>();
183
184 // First, add all the properties
185 foreach (PropertyInfo prop in type.GetProperties(bindingFlags).Where(p => p.DeclaringType == type))
186 {
187 typeProperties[prop.Name] = new Property() { PropertyInfo = prop };
188 }
189
190 // Now, add all the fields
191 foreach (FieldInfo field in type.GetFields(bindingFlags).Where(p => p.DeclaringType == type))
192 {
193 typeProperties[field.Name] = new Field() { FieldInfo = field };
194 }
195
196 // Finally, recurse on the base class to add its fields
197 if (type.BaseType != null)
198 {
199 foreach (IProperty prop in GetTypeProperties(type.BaseType).Values)
200 {
201 typeProperties[prop.Name] = prop;
202 }
203 }
204
205 // Cache it for next time
206 _propertiesOnType[type] = typeProperties;
207
208 return typeProperties;
209 }
210
211 private static object InvokeMemberOnType(Type type, object target, string name, object[] args)
212 {
213 try
214 {
215 // Try to incoke the method
216 return type.InvokeMember(
217 name,
218 BindingFlags.InvokeMethod | bindingFlags,
219 null,
220 target,
221 args);
222 }
223 catch (MissingMethodException)
224 {
225 // If we couldn't find the method, try on the base class
226 if (type.BaseType != null)
227 {
228 return InvokeMemberOnType(type.BaseType, target, name, args);
229 }
230
231 throw;
232 }
233 }
234 }

扩展方法如下
1 public static class PrivateReflectionDynamicObjectExtensions
2 {
3 public static dynamic AsDynamic(this object o)
4 {
5 return PrivateReflectionDynamicObject.WrapObjectIfNeeded(o);
6 }
7 }
最后调用拓展方法 var age = p1.AsDynamic().GetOtherPerson().OtherAge; 成功!所以有时候使用dynamic使得代码变得更加优美而用反射代码繁多而且显得非常臃肿,通过再一次学习dynamic,对此也深信不疑!
本文转自Jeffcky博客园博客,原文链接:http://www.cnblogs.com/CreateMyself/p/4680229.html,如需转载请自行联系原作者