利用反射以及特性实现实体类与格式字符串的相互转换

首先利用反射将实体类转换成字符串的简单应用

 1  1 using System;
 2  2 using System.Collections.Generic;
 3  3 using System.Linq;
 4  4 using System.Text;
 5  5 using System.Threading.Tasks;
 6  6 using System.Reflection;
 7  7 
 8  8 namespace StudyReflection
 9  9 {
10 10     class Program
11 11     {
12 12         static void Main(string[] args)
13 13         {
14 14             Entity entity = new Entity();
15 15             entity.name = "张三";
16 16             entity.password = "12354";
17 17             18             Console.WriteLine( EntityToString(entity) );
18 19         }
19 20 
20 21         #region 实体类-字符串        
21 22         /// <summary>
22 23         /// 通过反射将实体类转换为字符串
23 24         /// </summary>
24 25         /// <param name="entity"></param>
25 26         private static string EntityToString(Entity entity)
26 27         {
27 28             StringBuilder sb = new StringBuilder();
28 29             Type t = entity.GetType();//获取实体类类型
29 30             PropertyInfo[] pis = t.GetProperties();//获取该类型下的所有属性 
30 31             foreach(var pi in pis)//遍历属性
31 32             {                
32 33                     sb.Append(pi.Name + ":" + pi.GetValue(entity, null) + "|");//追加到字符串中               
33 34             }
34 35 
35 36             return sb.ToString().TrimEnd('|');
36 37         }
37 38         #endregion
38 39 
39 40     }
40 41 
41 42     #region 实体类    
42 43     /// <summary>
43 44     /// 实体类
44 45     /// </summary>
45 46     class Entity
46 47     {
47 48         
48 49         public string name { get; set; }
49 50 
50 51
51 52         public string password { get; set; }
52 53
53 54
54 55
55 56     }
56 57     #endregion
View Code

 

但是这样简单的只利用反射转换实体类会有一些问题 。此时,字符串的输出顺序取决于实体类里属性定义的先后顺序。如果更改了实体类里属性的先后定义顺序,那么,输出出来的字符串也会随之发生变化。

 

如果想要字符串按照自己想要的某种格式输出,又不受实体类定义的限制,就要使用到特性。

 

通过给实体类的每个属性都加上自定义的特性类,在遍历属性时先遍历各个属性头上的特性,将其按照某种顺序排列起来,再拼装成字符串输出。

  1   1 using System;
  2   2 using System.Collections.Generic;
  3   3 using System.Linq;
  4   4 using System.Text;
  5   5 using System.Threading.Tasks;
  6   6 using System.Reflection;
  7   7 using System.Collections;
  8   8 
  9   9 namespace StudyReflection
 10  10 {
 11  11     class Program
 12  12     {
 13  13         static void Main(string[] args)
 14  14         {
 15  15             Person person = new Person();
 16  16             person.name = "张三";
 17  17             person.password = "12354";
 18  18             person.date = DateTime.Now;
 19  19 
 20  20             Cat cat = new Cat();
 21  21             cat.name = "喵喵";
 22  22             cat.height = 20;
 23  23 
 24  24             Console.WriteLine( EntityToString(person) );
 25  25             Console.WriteLine(EntityToString(cat));
 26  26         }
 27  27 
 28  28         #region 实体类-字符串        
 29  29         /// <summary>
 30  30         /// 通过反射将实体类转换为字符串
 31  31         /// </summary>
 32  32         /// <param name="entity"></param>
 33  33         private static string EntityToString(Entity entity)
 34  34         {
 35  35             StringBuilder sb = new StringBuilder();
 36  36             Type t = entity.GetType();//获取实体类类型
 37  37 
 38  38             var dic = new Dictionary<int, PropertyInfo>();//定义字典,用于储存该类型的所有属性
 39  39             PropertyInfo[] pis = t.GetProperties();//获取该类型下的所有属性 
 40  40 
 41  41             foreach(var pi in pis)//遍历属性
 42  42             {
 43  43                 foreach (var att in pi.GetCustomAttributes(false))//遍历该属性的所有特性,并且不是从父类继承下来的
 44  44                 {
 45  45                     if (att is EntityAttribute)//判断该特性是否为自定义特性类EntityAttribute
 46  46                     {
 47  47                         var attr = att as EntityAttribute;
 48  48                         dic.Add(attr.number, pi);
 49  49                     }
 50  50                 }
 51  51             }
 52  52             //此时字典中已经存储了<编号,属性>,那么,只需要对编号进行排序,就可以按照顺序将属性的值添加到stringBuilder中
 53  53             var arr = dic.Keys.ToArray();
 54  54             Array.Sort(arr);
 55  55             foreach(var d in arr)
 56  56             {
 57  57                 sb.Append(dic[d].Name + ":" + dic[d].GetValue(entity, null) + "|");
 58  58             }
 59  59             return sb.ToString().TrimEnd('|');
 60  60         }
 61  61         #endregion
 62  62 
 63  63     }
 64  64 
 65  65     #region 实体类    
 66  66     /// <summary>
 67  67     /// 实体类
 68  68     /// </summary>
 69  69     abstract class Entity
 70  70     {     
 71  71         
 72  72     }
 73  73     class Person:Entity
 74  74     {
 75  75         [Entity(2)]
 76  76         public string password { get; set; }
 77  77 
 78  78         [Entity(1)]
 79  79         public string name { get; set; }
 80  80 
 81  81         [Entity(3)]
 82  82         public DateTime date { get; set; }
 83  83     }
 84  84     class Cat:Entity
 85  85     {       
 86  86         [Entity(2)]
 87  87         public int height { get; set; }
 88  88 
 89  89         [Entity(1)]
 90  90         public string name { get; set; }
 91  91     }
 92  92     #endregion
 93  93 
 94  94     #region 特性类    
 95  95     /// <summary>
 96  96     /// 自定义特性类
 97  97     /// 用于给实体类加上特性
 98  98     /// </summary>
 99  99     [AttributeUsage(AttributeTargets.Property)]
100 100     class EntityAttribute : Attribute
101 101     {
102 102         public EntityAttribute(int number)
103 103         {
104 104             this.number = number;
105 105         }
106 106         /// <summary>
107 107         /// 编号
108 108         /// </summary>
109 109         public int number { get; set; }
110 110     }
111 111     #endregion
112 112 
113 113 
114 114 }
View Code

 

这样的话,就可以自定义字符串的顺序而不受实体类属性先后定义的影响了。

 

在使用反射对实体类进行转换时,一定要分清楚实体类结构与反射的对应关系:

 

实体类本身       ------  Type t = entity.GetType()

实体类的所有属性    ------  PropertyInfo[] pis = t.GetProperties()

实体类的属性的特性   ------  GetCustomAttributes

 

只要了解了实体类结构与反射的关系,那么不管转换的实体类有多复杂,转换时也都轻而易举

例:在实体类中有集合,并且集合中放置的是一个对象的情况下

那么,在代码中可以这样实现:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Threading.Tasks;
  6 using System.Reflection;
  7 using System.Collections;
  8 using System.Xml;
  9 
 10 namespace StudyReflection
 11 {
 12     class Program
 13     {
 14         static void Main(string[] args)
 15         {
 16             PersonSon personSon1 = new PersonSon { name = "张三大儿子", password = "54321", date = DateTime.Now };
 17             PersonSon personSon2 = new PersonSon { name = "张三二儿子", password = "154354", date = DateTime.Now };
 18             Person person = new Person { name = "张三" , password = "12354" , date = DateTime.Now , listPersonSon = new List<PersonSon> { personSon1,personSon2} };
 19             
 20             //Cat cat = new Cat();
 21             //cat.name = "喵喵";
 22             //cat.height = 20;
 23             Program p = new Program();
 24             Console.WriteLine(EntityToString(person));
 25            // Console.WriteLine(EntityToString(cat));
 26         }
 27 
 28         #region 实体类-字符串        
 29         /// <summary>
 30         /// 通过反射将实体类转换为字符串
 31         /// </summary>
 32         /// <param name="entity"></param>
 33         private static string EntityToString(Entity entity)
 34         {
 35             StringBuilder sb = new StringBuilder();
 36             Type t = entity.GetType();//获取实体类类型
 37             var dic = new Dictionary<int, dynamic>();//定义字典,用于储存该类型的所有属性
 38             PropertyInfo[] pis = t.GetProperties();//获取该类型下的所有属性 
 39             foreach (var pi in pis)//遍历属性
 40             {
 41                 foreach (var att in pi.GetCustomAttributes(false))//遍历该属性的所有特性,并且不是从父类继承下来的
 42                 {
 43                     if (att is EntityAttribute)//判断该特性是否为自定义特性类EntityAttribute
 44                     {
 45                         var attr = att as EntityAttribute;
 46                         dic.Add(attr.number, new { Pro = pi,Att = attr});//此时存储的attr是为了在遍历字典时区分是否为List
 47                     }
 48                 }
 49             }
 50             //此时字典中已经存储了<编号,属性>,那么,只需要对编号进行排序,就可以按照顺序将属性的值添加到stringBuilder中
 51             foreach (var d in dic.Keys.OrderBy(s => s))
 52             {
 53                 var pro = dic[d].Pro as PropertyInfo;
 54                 var att = dic[d].Att as EntityAttribute;
 55                 if(att.isList)//判断该属性是否为List
 56                 {
 57                     StringBuilder sbList = new StringBuilder();
 58                     var list = pro.GetValue(entity, null) as ICollection;
 59                     foreach (var item in list)//遍历集合元素,
 60                     {
 61                         var itemType = item.GetType();//获取元素类型
 62                         var dicList = new Dictionary<int, dynamic>();
 63                         foreach (var itemPros in itemType.GetProperties())//遍历集合元素的所有属性
 64                         {
 65                             foreach (var itemPro in itemPros.GetCustomAttributes(false))//遍历该元素所有特性
 66                             {
 67                                 if (itemPro is EntityAttribute)
 68                                 {
 69                                     dicList.Add((itemPro as EntityAttribute).number, itemPros);
 70                                 }
 71                             }
 72                         }
 73                         foreach (var di in dicList.Keys.OrderBy(s => s))
 74                         {
 75                             var value = dicList[di] as PropertyInfo;
 76                             sbList.Append(value.Name + ":" + value.GetValue(item, null) + "|");
 77                         }
 78                         sbList =new StringBuilder( sbList.ToString().TrimEnd('|'));
 79                         sbList.Append("$");
 80                     }
 81                     sb.Append(sbList.ToString());//为了方便阅览,这里的List转String的方法写在了外面
 82                 }
 83                 else
 84                 {
 85                     sb.Append(pro.Name + ":" + pro.GetValue(entity, null) + "$");
 86                 }                
 87             }
 88             return sb.ToString().TrimEnd('$');
 89         }
 90         #endregion
 91 
 92     }
 93 
 94     #region 实体类    
 95     /// <summary>
 96     /// 实体类
 97     /// </summary>
 98     abstract class Entity
 99     {
100 
101     }
102     class Person : Entity
103     {
104         [Entity(2)]
105         public string password { get; set; }
106         [Entity(1)]
107         public string name { get; set; }
108         [Entity(3)]
109         public DateTime date { get; set; }
110         [Entity(4,isList = true)]
111         public List<PersonSon> listPersonSon { get; set; }
112     }
113 
114     class PersonSon : Entity
115     {
116         [Entity(2)]
117         public string password { get; set; }
118         [Entity(1)]
119         public string name { get; set; }
120         [Entity(3)]
121         public DateTime date { get; set; }
122     }
123 
124     class Cat : Entity
125     {
126         [Entity(2)]
127         public int height { get; set; }
128 
129         [Entity(1)]
130         public string name { get; set; }
131     }
132     #endregion
133 
134     #region 特性类    
135     /// <summary>
136     /// 自定义特性类
137     /// 用于给实体类加上特性
138     /// </summary>
139     [AttributeUsage(AttributeTargets.Property)]
140     class EntityAttribute : Attribute
141     {
142         public EntityAttribute(int number)
143         {
144             this.number = number;
145         }
146         /// <summary>
147         /// 编号
148         /// </summary>
149         public int number { get; set; }
150 
151         /// <summary>
152         /// 是否为集合
153         /// </summary>
154         public bool isList { get; set; }
155     }
156     #endregion
157 
158 
159 }
View Code

 

以上便是实体类转换成固定格式字符串的思路

下面来看看如何将固定格式的字符串转换成实体类  当然,字符串肯定是跟实体类有某种对应关系的,不然也不能相互转换了

首先, 在把字符串转换成实体类之前,我们肯定要知道这个用于接收字符串中的值的实体类的类型,从而才能创建实体类

Activator.CreateInstance(类型)  使用指定类型的默认构造函数来创建该类型的实例

其次,实体类中的属性有三种情况:1、属性  2、对象  3、泛型集合

属性最好解决,我们直接遍历该实体类的属性,然后给他赋值就行了

但是对于对象和泛型集合,我们肯定需要遍历他们自身的属性,然后给相对应的属性赋值

其中,我们会发现,其中遍历该实体类下的属性,所做的操作都是一样的,这个时候,就可以使用递归来简单实现这一步骤

完整代码如下:

 1 Entity BuildOutEnity(Type type, string content)//type代表实体类的类型,content代表字符串
 2         {
 3 
 4             var obj = Activator.CreateInstance(type);//创建type类型的实例对象---即最外层的实体类
 5             foreach (var att in type.GetCustomAttributes(false))
 6             {
 7                 if (att is SpliteCharAttribute)//SpliteCharAttribute,该特性类用于判断这个实体类在字符串中是用什么符号切割的
 8                 {
 9                     var attr = att as SpliteCharAttribute;
10                     var flag = attr.Flag;//此时flag即代表分割各个实体类的字符
11                     var outStringArr = content.Split(new string[] { flag }, StringSplitOptions.None);//利用flag将出参切割,
12                     var pros = new SortedDictionary<int, PropertyInfo>();
13                     foreach (var pro in type.GetProperties())
14                     {
15                         foreach (var patt in pro.GetCustomAttributes(false))
16                         {
17                             if (patt is EntityAttribute)//EntityAttribute,该实体类用于给属性头上加上序号的特性,从而一一对应给属性赋值
18                             {
19                                 var pattr = patt as EntityAttribute;
20                                 pros.Add(pattr.number, pro);//number即代表属性的序列号
21                             }
22                         }
23                     }
24 
25                     foreach (var proValue in pros)
26                     {
27                         var pro = proValue.Value;
28                         if (pro.PropertyType.IsGenericType)//判断该属性的类型是否为泛型类型
29                         {
30                             var list = Activator.CreateInstance(pro.PropertyType) as IList;
31                             var childContent = outStringArr[proValue.Key - 1];//字符串
32                             var childValues = childContent.Split(new string[] { "$" }, StringSplitOptions.None);
33                             for (var i = 0; i < childValues.Length; i++)
34                             {
35                                 var entityValue = BuildOutEnity(pro.PropertyType.GenericTypeArguments[0], childValues[i]);
36 
37                                 list.Add(entityValue);
38                             }
39 
40                             pro.SetValue(obj, list, null);
41 
42                         }
43                         else if (pro.PropertyType.IsSubclassOf(typeof(Entity)))//判断该属性类型是否继承自Entity的类型
44                         {
45                             var entityValue = BuildOutEnity(pro.PropertyType, outStringArr[proValue.Key - 1]);
46                             pro.SetValue(obj, entityValue, null);
47                         }
48                         else//剩余情况就剩下属性了,直接赋值
49                         {
50                             var value = Convert.ChangeType(outStringArr[proValue.Key - 1], pro.PropertyType);
51                             //处理数据类型为零的情况
52                             if (pro.PropertyType == typeof(decimal) || pro.PropertyType == typeof(double) || pro.PropertyType == typeof(float) || pro.PropertyType == typeof(int) || pro.PropertyType == typeof(short) || pro.PropertyType == typeof(long) || pro.PropertyType == typeof(uint))
53                             {
54                                 if (string.IsNullOrEmpty(outStringArr[proValue.Key - 1]))
55                                 {
56                                     outStringArr[proValue.Key - 1] = "0";
57                                 }
58                             }
59                             pro.SetValue(obj, value, null);
60                         }
61                     }
62                 }
63 
64             }
65             return obj as Entity;
66         }
View Code

 

 

 

以上全部,即是字符串与实体类之间的相互转换

 

posted @ 2017-10-13 17:01  吴祥锋  阅读(1402)  评论(0)    收藏  举报