同樣,你必須寫程式碼對集合排序,集合元素為添加 DefaultSort 特性的物件。用反射發現正確屬性,比較兩不同物件屬性值。好消息:只寫一次這樣的程式碼。
下一步,寫實作 IComparer 的類別(原則 26 詳細討論比較)。IComparer 有 Compare() 比較兩個給定型別物件。將特性放在實作 IComparable 的類別上,定義排序順序。建構函式針對通用比較發現預設排序屬性標記,基於已比較型別。Compare 方法對任何型別兩物件排序,使用預設排序屬性:
internal class GenericComparer : IComparer
{
private readonly PropertyDescriptor _sortProp;
private readonly bool _reverse = false;
public GenericComparer(Type t) : this(t, false) { }
public GenericComparer(Type t, bool reverse)
{
_reverse = reverse;
object[] a = t.GetCustomAttributes(typeof(DefaultSortAttribute), false);
if (a.Length > 0)
{
DefaultSortAttribute sortName = a[0] as DefaultSortAttribute;
string name = sortName.Name;
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(t);
if (props.Count > 0)
{
foreach(PropertyDescriptor p in props)
if (p.Name == name) { _sortProp = p; break; }
}
}
}
int IComparer.Compare(object left, object right)
{
if ((left == null) && (right == null)) return 0;
if (left == null) return -1;
if (right == null) return 1;
if (_sortProp == null) return 0;
IComparable lField = _sortProp.GetValue(left) as IComparable;
IComparable rField = _sortProp.GetValue(right) as IComparable;
int rVal = 0;
if (lField == null)
if (rField == null) return 0;
else return -1;
rVal = lField.CompareTo(rField);
return (_reverse) ? -rVal : rVal;
}
}
此通用比較器對任何標記 DefaultSort 的 Customers 集合可排序:
CustomerList.Sort(new GenericComparer(typeof(Customer)));
GenericComparer 程式碼利用進階技術、反射(原則 43)。但只寫一次。之後只需添加特性到其他類別,即可對這些物件集合進行有用排序。修改 DefaultSort 參數即修改類別行為,無需修改所有演算法。
當簡單宣告能說明意圖時,宣告式習慣很有用,幫助避免重複程式碼。參考 GenericComparer,你應能為任何建立的型別寫出不同(直接了當)的排序演算法。宣告式程式設計好處:只寫一次通用型別,再用簡單宣告為每個型別建立行為。關鍵:行為改變基於單一宣告,非基於演算法。GenericComparer 可在任何用 DefaultSort 修飾的型別上工作。若程式只需一兩次排序,按常規簡單方法寫。但若對同樣行為需在幾十個型別上實作,通用演算法與宣告式方案省下大量時間,長期運行也有力。不應為 WebMethod 特性寫所有程式碼,應將此技術展開在自有演算法上。原則 42 討論用特性建立附加命令處理常式。其他例子可能包括定義附加套件建立動態 Web UI 頁面。
宣告式程式設計是強大工具,當可用特性表明意圖時,可減少大量類似手寫演算法中邏輯錯誤可能。建立更易讀、清晰程式碼,意味著現在或未來少出錯。若可用 .NET 框架定義特性,直接用。否則考慮建立自有特性,未來用它建立相同行為。