使用Attribute+Reflection來提高代碼重用

來源:互聯網
上載者:User

這篇文章兩個目的,一是開闊設計的思路,二是執行個體代碼可以拿來就用。

設計的思路來源於《Effective c#》第一版Item 24: 優先使用聲明式編程而不是命令式編程。特別 的地方是,希望提供多個屬性的預設排序,而不僅僅只根據一個屬性,另外一點是,優先調用對象屬性 實現了的IComparable<T>介面,如果沒有實現介面,才調用IComparable進行比較。排序類實現 泛型,得到型別安全。

總的思路:Attribute用來裝飾我們想要擷取中繼資料的類,使用Reflection來提取中繼資料,根據提取 到的中繼資料實現一些和對象無關的組件功能。

那麼,這個例子要實現的效果是用Attribute裝飾類對象,設定該對象的預設排序屬性,排序的時候 ,根據這些預設排序來進行排序。

[DefaultSort(new string[] {"ID", "Name"})]class SortData{    public int ID { get; set; }       public string Name { get; set; }       public string Value { get; set; }       public override string ToString()    {        return String.Format("ID:{0},Name:{1},Value:{2}", ID, Name, Value);    }}

對於SortData對象來說,我們希望根據它的ID來排序,如果ID相等,再根據Name屬性來排序。像它 的名字暗示的一樣,這是預設的行為,不需要我們實現SortData的IComparable<SortData>介面 ,將來要改變定序,只要修改DefaultSort中屬性名稱數組的內容就夠了,很方便。

原書中記錄的DefaultAttribute只能根據一個屬性名稱來排序,不夠實用,希望它像下面的類一樣 ,能記錄多個屬性的名稱。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple=false)]    public class DefaultSortAttribute : System.Attribute    {        private string[] m_propNames;           public string[] PropNames        {            get { return m_propNames; }            set { m_propNames = value; }        }           public DefaultSortAttribute(string propName)        {            m_propNames = new string[] { propName };        }           public DefaultSortAttribute(string[] propNames)        {            m_propNames = propNames;        }    }

注意仍然保留了只希望拿一個屬性來排序的建構函式,對類進行裝飾時,往類上面放[DefaultSort (new string[] {"ID", "Name"})] 和[DefaultSort("ID")]類似的聲 明就夠了。

既然使用Attribute裝飾了類,就要知道這樣的中繼資料,下面需要採用Reflection讀到要排序的預設 屬性名稱,相對於原書中的改進是,使用泛型和優先使用屬性的IComparable<T>介面來比較排序。

using System;using System.Linq;using System.Collections;using System.Collections.Generic;using System.ComponentModel;using System.Reflection;   namespace ProJKY.Extensions{    public class DefaultSortComparer<T> : IComparer, IComparer<T>    {        private readonly PropertyDescriptor[] m_sortProps;        private readonly bool m_reverse = false;        private readonly bool m_valueType = false;           public DefaultSortComparer() :            this(false)        { }           public DefaultSortComparer(bool reverse)        {            m_reverse = reverse;            Type t = typeof(T);            m_valueType = t.IsValueType;               object[] a = t.GetCustomAttributes(typeof(DefaultSortAttribute), false);               // 強制檢查,不支援沒有用DefaultSortAttribute裝飾的類            if (a.Length != 1)                throw new NotSupportedException(t.Name);               DefaultSortAttribute sortName = a[0] as DefaultSortAttribute;               string[] propNames = sortName.PropNames;               m_sortProps = new PropertyDescriptor[propNames.Length];               PropertyDescriptorCollection props = TypeDescriptor.GetProperties(t);               for (int i = 0; i < propNames.Length; i++){                foreach (PropertyDescriptor p in props){                    if (p.Name == propNames[i]){                    m_sortProps[i] = p;                    break;                    }                }            }        }           int IComparer.Compare(object left, object right)        {            if (HasNull(left, right) == true)            {                int nullCompare = CompareWithNull(left, right);                   return m_reverse ? -nullCompare : nullCompare;            }               if (left.GetType() != right.GetType())                throw new ArgumentException("left and right not match.");               if (typeof(T).IsAssignableFrom(left.GetType()) == false)                throw new ArgumentException("type not compatible.");               return Compare((T)left, (T)right);        }           public int Compare(T x, T y)        {            if (m_valueType == false && HasNull(x, y) == true){                int nullCompare = CompareWithNull(x, y);                return m_reverse ? -nullCompare : nullCompare;            }               foreach (var prop in m_sortProps){                object xValue = prop.GetValue(x);                object yValue = prop.GetValue(y);                   if (HasNull(xValue, yValue) == true){                    int nullCompare = CompareWithNull(xValue, yValue);                       return m_reverse ? -nullCompare : nullCompare;                }                   Type propType = xValue.GetType();                   // 優先使用IComaprable<T>介面                if (typeof(IComparable<>).MakeGenericType(propType).IsAssignableFrom(propType))                {                    MethodInfo methodInfo = propType.GetMethods().FirstOrDefault(method => method.Name == "CompareTo"                        && method.GetParameters().Length == 1                        && method.GetParameters()[0].ParameterType == propType);                       int gretValue = (int)methodInfo.Invoke(xValue, new object[] { yValue });                       if (gretValue == 0) continue;                       return m_reverse ? -gretValue : gretValue;                }                   IComparable xNonGeneric = xValue as IComparable;                IComparable yNonGeneric = yValue as IComparable;                   if (xNonGeneric == null)                    throw new ArgumentException("Property " + prop.Name + " is not comparable.");                   int retValue = xNonGeneric.CompareTo(yValue);                   if (retValue == 0) continue;                   return m_reverse ? -retValue : retValue;            }               return 0;        }           int CompareWithNull(object left, object right)        {            if ((left == null) && (right == null))                return 0;               if (left == null)                return -1;               return 1;        }           bool HasNull(object left, object right)        {            if (left == null || right == null)                return true;            return false;        }    }}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.