程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> 使用Attribute+Reflection來提高代碼重用

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

編輯:關於C#

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

設計的思路來源於《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;
        }
    }
}

需要注意的是DefaultSortComparer<T>是泛型的,並實現了IComparer, IComparer<T> 接口,實現了這兩個接口,才方便排序。

代碼寫貼了這麼多,用起來怎麼用呢。

var data1 = new SortData() { ID = 1, Name = "info", Value = "key"};
var data2 = new SortData() { ID = 3, Name = "64File", Value = "license" };
var data3 = new SortData() { ID = 2, Name = "cloneToken", Value = "comparer" };
var data4 = new SortData() { ID = 1, Name = "0est", Value = "backend" 

};
   
List<SortData> sortData = new List<SortData>();
sortData.Add(data1);
sortData.Add(data2);
sortData.Add(data3);
sortData.Add(data4);
   
sortData.Sort(new DefaultSortComparer<SortData>(false));
   
sortData.ForEach(data => Console.WriteLine(data));

結果就不獻丑了,經測試,能正常工作。

通過這個例子,就可以看到,要實現它的關鍵是,Attribute負責裝飾類,Reflection負責讀取特定 Attribute裝飾後的元數據信息,實現和特定類類型無關的組件。一次Coding,多次復用。

希望大家多支持,以後會多放一些有意思的開箱即用的代碼上來。

查看本欄目

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved