开发者社区> 嗯哼9925> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

反射之动态创建对象

简介:
+关注继续查看

前言

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 dynew 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,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
注解和反射12.动态创建对象执行方法
注解和反射12.动态创建对象执行方法
0 0
反射实例化对象|学习笔记
快速学习反射实例化对象
0 0
反射之动态创建对象
前言 C#有关反射的话题已经是个老生常谈的话题,也许园友一看这标题都不屑去看了,但是既然拿出来讲必有讲之道理,当然,不喜勿喷,高手请绕道!直入话题。 讨论  定义一个Person类代码如下 1 public class Person 2 { 3 4 ...
480 0
c# 利用反射获得某个类或者对象的所有属性
今天碰到一个类,总共有60个列及属性 我做的一个功能需要把这些属性放到一个数组里 一想要用new string[]{"","",....}的话写死了·· 就想到用反射来做吧,如果属性在多的话也不会影响代码(不过都说反射对性能影响比较厉害我这用的少,没感觉) 正文开始: IList prop...
407 0
动态创建对象
//反射利用无参构造创建对象 //无参 //------------------------------------------------------------------------------   //1 Assembly的CreateInstance方法。
521 0
[转载]利用反射动态创建对象
利用反射动态创建对象 (转自张逸的blog) 前两天我发了一篇文章《通过反射动态实例化对象中出现的一个奇怪问题》,对反射中的某些问题疑惑不解。通过这几天不断查看MSDN,上网查询,现在终于解决了该问题。
857 0
[转载]使用反射技术动态创建类对象(实例代码)
经过数天的研究,终于能动态加载数据访问层了。虽然网上有不少介绍反射的文章,但都是从理论上来说的,没有一个特别详细的例子,所以一直没能写出实际代码。这里把自己的一段代码写出来,希望能够帮助像我一样的初学者快速上手,先应用,再深入,免得着急。
533 0
+关注
文章
问答
文章排行榜
最热
最新
相关电子书
更多
建立联系方法之一
立即下载
JAVA反射原理以及一些常见的应用
立即下载
低代码开发师(初级)实战教程
立即下载