前言
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】利用构造器来动态创建对象