首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 實戰:用 Attribute + Reflection 實現多欄位默認排序

     編輯:關於C#

背景與目標

參考《Effective C#》Item 24「優先聲明式編程」,希望為類提供多屬性默認排序,且優先調用屬性實現的 IComparable<T>,回退至非泛型 IComparable。通過泛型實現類型安全,一次編寫、多處復用。

核心設計

  1. Attribute 裝飾類:標記默認排序屬性名陣列
  2. Reflection 讀取元數據:運行時獲取屬性描述符
  3. 通用比較器:實現 IComparer/IComparer<T>,按屬性序列比較

關鍵代碼

1. DefaultSortAttribute

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

2. 被排序類

[DefaultSort(new[] { "ID", "Name" })]
public class SortData {
    public int ID { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }
    public override string ToString() => $"ID:{ID},Name:{Name},Value:{Value}";
}

3. DefaultSortComparer<T>

public class DefaultSortComparer : IComparer, IComparer {
    private readonly PropertyDescriptor[] _sortProps;
    private readonly bool _reverse;
    private readonly bool _isValueType;

    public DefaultSortComparer(bool reverse = false) {
        _reverse = reverse;
        var t = typeof(T);
        _isValueType = t.IsValueType;
        var attrs = t.GetCustomAttributes(typeof(DefaultSortAttribute), false);
        if (attrs.Length != 1) throw new NotSupportedException(t.Name);
        var propNames = ((DefaultSortAttribute)attrs[0]).PropNames;
        var props = TypeDescriptor.GetProperties(t);
        _sortProps = propNames.Select(n => props.Find(n, true)).ToArray();
    }

    public int Compare(T x, T y) {
        // null 檢查、值類型處理略
        foreach (var prop in _sortProps) {
            var xVal = prop.GetValue(x);
            var yVal = prop.GetValue(y);
            // null 比較略
            var propType = xVal.GetType();
            // 優先嘗試 IComparable
            var genComparable = typeof(IComparable<>).MakeGenericType(propType);
            if (genComparable.IsAssignableFrom(propType)) {
                var m = propType.GetMethod("CompareTo", new[] { propType });
                var res = (int)m.Invoke(xVal, new[] { yVal });
                if (res != 0) return _reverse ? -res : res;
                continue;
            }
            // 回退非泛型 IComparable
            if (xVal is IComparable cmp) {
                var res = cmp.CompareTo(yVal);
                if (res != 0) return _reverse ? -res : res;
            } else throw new ArgumentException($"Property {prop.Name} not comparable");
        }
        return 0;
    }
    // 非泛型 IComparer.Compare 實現略
}

使用示例

var list = new List {
    new SortData { ID = 1, Name = "info", Value = "key" },
    new SortData { ID = 3, Name = "64File", Value = "license" },
    new SortData { ID = 2, Name = "cloneToken", Value = "comparer" },
    new SortData { ID = 1, Name = "0est", Value = "backend" }
};
list.Sort(new DefaultSortComparer(false));
list.ForEach(Console.WriteLine);

輸出先按 ID 升序,ID 相同再按 Name 升序。

優勢總結

  • 聲明式:修改 Attribute 即可調整排序規則,無需修改類代碼
  • 類型安全:泛型比較器 + 優先 IComparable<T> 避免裝箱/拆箱
  • 靈活:支援逆序、值類型、多屬性、null 處理
  • 解耦:排序邏輯與業務類完全分離
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved