zoukankan      html  css  js  c++  java
  • Attribute+Reflection,提高代码重用

    这篇文章两个目的,一是开阔设计的思路,二是实例代码可以拿来就用。

    设计的思路来源于《Effective c#》第一版Item 24: 优先使用声明式编程而不是命令式编程。特别的地方是,希望提供多个属性的默认排序,而不仅仅只根据一个属性,另外一点是,优先调用对象属性实现了的IComparable<T>接口,如果没有实现接口,才调用IComparable进行比较。排序类实现泛型,得到类型安全。

    总的思路:Attribute用来装饰我们想要获取元数据的类,使用Reflection来提取元数据,根据提取到的元数据实现一些和对象无关的组件功能。

    那么,这个例子要实现的效果是用Attribute装饰类对象,设置该对象的默认排序属性,排序的时候,根据这些默认排序来进行排序。

     1 [DefaultSort(new string[] {"ID", "Name"})]
     2 class SortData
     3 {
     4     public int ID { get; set; }
     5 
     6     public string Name { get; set; }
     7 
     8     public string Value { get; set; }
     9 
    10     public override string ToString()
    11     {
    12         return String.Format("ID:{0},Name:{1},Value:{2}", ID, Name, Value);
    13     }
    14 }

    对于SortData对象来说,我们希望根据它的ID来排序,如果ID相等,再根据Name属性来排序。像它的名字暗示的一样,这是默认的行为,不需要我们实现SortData的IComparable<SortData>接口,将来要改变排序规则,只要修改DefaultSort中属性名称数组的内容就够了,很方便。

    原书中记录的DefaultAttribute只能根据一个属性名称来排序,不够实用,希望它像下面的类一样,能记录多个属性的名称。

     1 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple=false)]
     2     public class DefaultSortAttribute : System.Attribute
     3     {
     4         private string[] m_propNames;
     5 
     6         public string[] PropNames
     7         {
     8             get { return m_propNames; }
     9             set { m_propNames = value; }
    10         }
    11 
    12         public DefaultSortAttribute(string propName)
    13         {
    14             m_propNames = new string[] { propName };
    15         }
    16 
    17         public DefaultSortAttribute(string[] propNames)
    18         {
    19             m_propNames = propNames;
    20         }
    21     }

    注意仍然保留了只希望拿一个属性来排序的构造函数,对类进行装饰时,往类上面放[DefaultSort(new string[] {"ID", "Name"})] 和[DefaultSort("ID")]类似的声明就够了。

    既然使用Attribute装饰了类,就要知道这样的元数据,下面需要采用Reflection读到要排序的默认属性名,相对于原书中的改进是,使用泛型和优先使用属性的IComparable<T>接口来比较排序。

      1 using System;
      2 using System.Linq;
      3 using System.Collections;
      4 using System.Collections.Generic;
      5 using System.ComponentModel;
      6 using System.Reflection;
      7 
      8 namespace ProJKY.Extensions
      9 {
     10     public class DefaultSortComparer<T> : IComparer, IComparer<T>
     11     {
     12         private readonly PropertyDescriptor[] m_sortProps;
     13         private readonly bool m_reverse = false;
     14         private readonly bool m_valueType = false;
     15 
     16         public DefaultSortComparer() :
     17             this(false)
     18         { }
     19 
     20         public DefaultSortComparer(bool reverse)
     21         {
     22             m_reverse = reverse;
     23             Type t = typeof(T);
     24             m_valueType = t.IsValueType;
     25 
     26             object[] a = t.GetCustomAttributes(typeof(DefaultSortAttribute), false);
     27 
     28             // 强制检查,不支持没有用DefaultSortAttribute装饰的类
     29             if (a.Length != 1)
     30                 throw new NotSupportedException(t.Name);
     31 
     32             DefaultSortAttribute sortName = a[0] as DefaultSortAttribute;
     33 
     34             string[] propNames = sortName.PropNames;
     35 
     36             m_sortProps = new PropertyDescriptor[propNames.Length];
     37 
     38             PropertyDescriptorCollection props = TypeDescriptor.GetProperties(t);
     39 
     40             for (int i = 0; i < propNames.Length; i++){
     41                 foreach (PropertyDescriptor p in props){
     42                     if (p.Name == propNames[i]){
     43                     m_sortProps[i] = p;
     44                     break;
     45                     }
     46                 }
     47             }
     48         }
     49 
     50         int IComparer.Compare(object left, object right)
     51         {
     52             if (HasNull(left, right) == true)
     53             {
     54                 int nullCompare = CompareWithNull(left, right);
     55 
     56                 return m_reverse ? -nullCompare : nullCompare;
     57             }
     58 
     59             if (left.GetType() != right.GetType())
     60                 throw new ArgumentException("left and right not match.");
     61 
     62             if (typeof(T).IsAssignableFrom(left.GetType()) == false)
     63                 throw new ArgumentException("type not compatible.");
     64 
     65             return Compare((T)left, (T)right);
     66         }
     67 
     68         public int Compare(T x, T y)
     69         {
     70             if (m_valueType == false && HasNull(x, y) == true){
     71                 int nullCompare = CompareWithNull(x, y);
     72                 return m_reverse ? -nullCompare : nullCompare;
     73             }
     74 
     75             foreach (var prop in m_sortProps){
     76                 object xValue = prop.GetValue(x);
     77                 object yValue = prop.GetValue(y);
     78 
     79                 if (HasNull(xValue, yValue) == true){
     80                     int nullCompare = CompareWithNull(xValue, yValue);
     81 
     82                     return m_reverse ? -nullCompare : nullCompare;
     83                 }
     84 
     85                 Type propType = xValue.GetType();
     86 
     87                 // 优先使用IComaprable<T>接口
     88                 if (typeof(IComparable<>).MakeGenericType(propType).IsAssignableFrom(propType))
     89                 {
     90                     MethodInfo methodInfo = propType.GetMethods().FirstOrDefault(method => method.Name == "CompareTo"
     91                         && method.GetParameters().Length == 1
     92                         && method.GetParameters()[0].ParameterType == propType);
     93 
     94                     int gretValue = (int)methodInfo.Invoke(xValue, new object[] { yValue });
     95 
     96                     if (gretValue == 0) continue;
     97 
     98                     return m_reverse ? -gretValue : gretValue;
     99                 }
    100 
    101                 IComparable xNonGeneric = xValue as IComparable;
    102                 IComparable yNonGeneric = yValue as IComparable;
    103 
    104                 if (xNonGeneric == null)
    105                     throw new ArgumentException("Property " + prop.Name + " is not comparable.");
    106 
    107                 int retValue = xNonGeneric.CompareTo(yValue);
    108 
    109                 if (retValue == 0) continue;
    110 
    111                 return m_reverse ? -retValue : retValue;
    112             }
    113 
    114             return 0;
    115         }
    116 
    117         int CompareWithNull(object left, object right)
    118         {
    119             if ((left == null) && (right == null))
    120                 return 0;
    121 
    122             if (left == null)
    123                 return -1;
    124 
    125             return 1;
    126         }
    127 
    128         bool HasNull(object left, object right)
    129         {
    130             if (left == null || right == null)
    131                 return true;
    132             return false;
    133         }
    134     }
    135 }

    需要注意的是DefaultSortComparer<T>是泛型的,并实现了IComparer, IComparer<T>接口,实现了这两个接口,才方便排序。


    代码写贴了这么多,用起来怎么用呢。

     1 var data1 = new SortData() { ID = 1, Name = "info", Value = "key"};
     2 var data2 = new SortData() { ID = 3, Name = "64File", Value = "license" };
     3 var data3 = new SortData() { ID = 2, Name = "cloneToken", Value = "comparer" };
     4 var data4 = new SortData() { ID = 1, Name = "0est", Value = "backend" };
     5 
     6 List<SortData> sortData = new List<SortData>();
     7 sortData.Add(data1);
     8 sortData.Add(data2);
     9 sortData.Add(data3);
    10 sortData.Add(data4);
    11 
    12 sortData.Sort(new DefaultSortComparer<SortData>(false));
    13 
    14 sortData.ForEach(data => Console.WriteLine(data));


    结果就不献丑了,经测试,能正常工作。

    通过这个例子,就可以看到,要实现它的关键是,Attribute负责装饰类,Reflection负责读取特定Attribute装饰后的元数据信息,实现和特定类类型无关的组件。一次Coding,多次复用。

    希望大家多支持,以后会多放一些有意思的开箱即用的代码上来。

  • 相关阅读:
    数据挖掘相关资料收集(持续更新)
    常见面试之机器学习算法思想简单梳理
    在c中保存状态
    lua 和 c
    lua 基础库
    lua 面向对象
    lua 高级
    lua 基础
    lua中的协程
    cocos2d中的可见性检测
  • 原文地址:https://www.cnblogs.com/ProJKY/p/AttributeAndReflection.html
Copyright © 2011-2022 走看看