程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> Effective C#原則24:選擇申明式編程而不是命令式編程

Effective C#原則24:選擇申明式編程而不是命令式編程

編輯:關於C#

與命令式編程相比,申明式編程可以用更簡單,更清楚的方法來描述軟件的 行為。申明式編程就是說用申明來定義程序的行為,而不是寫一些指令。在C#裡 ,也和其它大多數語言一樣,你的大多數程序都是命令式的:在程序中寫一個方 法來定義行為。在C#中,你在編程時使用特性就是申明式編程。你添加一個特性 到類,屬性,數據成員,或者是方法上,然後.Net運行時就會為你添加一些行為 。這樣申明的目的就是簡單易用,而且易於閱讀和維護。

讓我們以一個 你已經使用過的例子開始。當你寫你的第一個ASP.Net Web服務時,向導會生成 這樣的代碼:

[WebMethod]
public string HelloWorld()
{
 return "Hello World";
}

VS.net的Web服務向導添加了[WebMethod]特性到HelloWorld()方 法上,這就定義了HelloWorld是一個web方法。ASP.net運行時會為你生成代碼來 響應這個特性。運行時生成的Web服務描述語言(WSDL)文檔,也就是包含了對 SOAP進行描述的文檔,調用HelloWorld方法。ASP.net也支持運行時發送SOAP請 求HelloWorld方法。另外,ASP.net運行時動態的生成HTML面頁,這樣可以讓你 在IE裡測試你的新Web服務。而這些全部是前面的WebMethod特性所響應的。這個 特性申明了你的意圖,而且運行時確保它是被支持的。使用特性省了你不少時間 ,而且錯誤也少了。

這並不是一個神話,ASP.net運行時使用反射來斷定 類裡的哪些方法是web服務,當它們發現這些方法時,ASP.net運行時就添加一些 必須的框架代碼到這些方法上,從而使任何添加了這些代碼的方法成為web方法 。

[WebMethod] 特性只是.Net類庫眾多特性之一,這些特性可能幫助你 更快的創建正確的程序。有一些特性幫助你創建序列化類型(參見原則25)。正如 你在原則4裡看到的,特性可以控制條件編譯。在這種情況以下其它一些情況下 ,你可以使用申明式編程寫出你所要的更快,更少錯誤的代碼。

你應該 使用.Net框架裡自帶的一些特性來申明你的意圖,這比你自己寫要好。因為這樣 花的時間少,更簡單,而且編譯器也不會出現錯誤。

如果預置的特性不 適合你的需求,你也可以通過定義自己的特性和使用反射來使用申明式編程結構 。做為一個例子,你可以創建一個特性,然而關聯到代碼上,讓用戶可以使用這 個特性來創建默認可以排序的類型。一個例子演示了如何添加這個特性,該特性 定義了你想如何在一個客戶集合中排序:

[DefaultSort( "Name" )]
public class Customer
{
 public string Name
 {
  get { return _name; }
  set { _name = value; }
 }
 public decimal CurrentBalance
  {
  get { return _balance; }
 }
 public decimal AccountValue
 {
  get
  {
   return calculateValueOfAccount();
  }
 }
}

DefaultSort特性,Nane屬性,這就暗示了任何Customer的集合 應該以客戶名字進行排序。DefaultSort特性不是.Net框架的一部份,為了實現 它,你創建一個DefaultSortAttribute類:

[AttributeUsage( AttributeTargets.Class |
 AttributeTargets.Struct )]
public class DefaultSortAttribute : System.Attribute
{
 private string _name;
 public string Name
 {
  get { return _name; }
  set { _name = value; }
 }
 public DefaultSortAttribute( string name )
 {
  _name = name;
 }
}

同樣,你還必須寫一些代碼,來對一個集合運行排 序,而該集合中的元素是添加了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框架裡定義的特 性,那就直接使用。如果不能,考慮選擇創建你自己的特性,這樣你可以在將來 使用它來創建同樣的行為。

返回教程目錄

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