同樣,你還必須寫一些代碼,來對一個集合運行排 序,而該集合中的元素是添加了DefaultSort特性的對象。你將用到反射來發現 正確的屬性,然後比較兩個不同對象的屬性值。一個好消息是你只用寫一次這樣 的代碼。
下一步,你要寫一個實現了IComparer接口的類。(在原則26中 會詳細的充分討論比較。) ICompare有一個CompareTo()方法來比較兩個給定類 型的對象,把特性放在實現了IComparable的類上,就可以定義排序順序了。構 造函數對於通用的比較,可以發現默認的排序屬性標記,而這個標記是基於已經 比較過的類型。Compare方法對任何類型的兩個對象進行排序,使用默認的排序 屬性:
internal class GenericComparer : IComparer
{
// Information about the default property:
private readonly PropertyDescriptor _sortProp;
// Ascending or descending.
private readonly bool _reverse = false;
// Construct for a type
public GenericComparer( Type t ) :
this( t, false )
{
}
// Construct for a type
// and a direction
public GenericComparer( Type t, bool reverse )
{
_reverse = reverse;
// find the attribute,
// and the name of the sort property:
// Get the default sort attributes on the type:
object [] a = t.GetCustomAttributes(
typeof( DefaultSortAttribute ),false );
// Get the PropertyDescriptor for that property:
if ( a.Length > 0 )
{
DefaultSortAttribute sortName = a[ 0 ] as DefaultSortAttribute;
string name = sortName.Name;
// Initialize the sort property:
PropertyDescriptorCollection props =
TypeDescriptor.GetPropertIEs( t );
if ( props.Count > 0 )
{
foreach ( PropertyDescriptor p in props )
{
if ( p.Name == name )
{
// Found the default sort property:
_sortProp = p;
break;
}
}
}
}
}
// Compare method.
int IComparer.Compare( object left,
object right )
{
// null is less than any real object:
if (( left == null ) && ( right == null ))
return 0;
if ( left == null )
return -1;
if ( right == null )
return 1;
if ( _sortProp == null )
{
return 0;
}
// Get the sort property from each object:
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;
}
}
這個通用的比較 對任何Customers 集合可以進行排序,而這個Customers是用DefaultSort特性申 明了的:
CustomerList.Sort( new GenericComparer(
typeof( Customer )));
實現GenericComparer的代碼利用了一些 高級的技術,使用反射(參見原則43)。但你必須寫一遍這樣的代碼。從這個觀點 上看,你所要做的就是添加空上屬性到其它任何類上,然而你就可以對這些對象 的集合進行能用的排序了。如果你修改了DefaultSort特性的參數,你就要修改 類的行為。而不用修改所有的算法。
這種申明式習慣是很有用的,當一 個簡單的申明可以說明你的意圖時,它可以幫助你避免重復的代碼。再參考 GenericComparer類,你應該可以為你創建的任何類型,寫一個不同的(而且是是 直接了當的)排序算法。這種申明式編程的好處就是你只用寫一次能用的類型, 然後就可以用一個簡單的申明為每個類型創建行為。關鍵是行為的改變是基於單 個申明的,不是基於任何算法的。GenericComparer可以在任何用DefaultSort特 性修飾了的類型上工作,如果你只須要在程序裡使用一兩次排序功能,就按常規 簡單的方法寫吧。然而,如果你的程序對於同樣的行為,可能須要在幾十個類型 上實現,那麼能用的算法以及申明式的解決方案會省下很多時間,而且在長時間 的運行中也是很有力的。你不應該為WebMethod特性寫代全部的代碼,你應該把 這一技術展開在你自己的算法上。原則42裡討論了一個例子:如何使用特性來建 立一個附加命令句柄。其它的例子可能還包括一些在定義附加包建立動態的web UI面頁時的其它內容。
申明式編程是一個很有力的工具,當你可以使用 特性來表明你的意圖時,你可以通過使用特性,來減少在大量類似的手寫算法中 出現邏輯錯誤的可能。申明式編程創建了更易於閱讀,清晰的代碼。這也就意味 著不管是現在還是將來,都會少出現錯誤。如果你可以使用.Net框架裡定義的特 性,那就直接使用。如果不能,考慮選擇創建你自己的特性,這樣你可以在將來 使用它來創建同樣的行為。
返回教程目錄