利用反射以及特性实现实体类与格式字符串的相互转换
首先利用反射将实体类转换成字符串的简单应用
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
但是这样简单的只利用反射转换实体类会有一些问题 。此时,字符串的输出顺序取决于实体类里属性定义的先后顺序。如果更改了实体类里属性的先后定义顺序,那么,输出出来的字符串也会随之发生变化。
如果想要字符串按照自己想要的某种格式输出,又不受实体类定义的限制,就要使用到特性。
通过给实体类的每个属性都加上自定义的特性类,在遍历属性时先遍历各个属性头上的特性,将其按照某种顺序排列起来,再拼装成字符串输出。
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 }
这样的话,就可以自定义字符串的顺序而不受实体类属性先后定义的影响了。
在使用反射对实体类进行转换时,一定要分清楚实体类结构与反射的对应关系:
实体类本身 ------ 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 }
以上便是实体类转换成固定格式字符串的思路
下面来看看如何将固定格式的字符串转换成实体类 当然,字符串肯定是跟实体类有某种对应关系的,不然也不能相互转换了
首先, 在把字符串转换成实体类之前,我们肯定要知道这个用于接收字符串中的值的实体类的类型,从而才能创建实体类
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 }
以上全部,即是字符串与实体类之间的相互转换

浙公网安备 33010602011771号