天天看點

泛型最佳實踐C#

泛型最佳實踐C#

什麼時候我不應該使用泛型?對泛型我應該使用什麼命名規範?我應該在泛型接口上面添加限制嗎?

泛型最佳實踐C#

如何處置(Dispose)泛型接口?可以對一般類型參數進行類型轉換嗎?

泛型最佳實踐C#

對泛型類如何同步多線程通路?如何序列化泛型類?

泛型最佳實踐C#
泛型最佳實踐C#

什麼時候我不應該使用泛型?

泛型最佳實踐C#
泛型最佳實踐C#

        不使用泛型的主要原因就是跨目标(cross-targeting)——如果你要在.NET 1.1和.NET 2.0下編譯相同的代碼,那麼由于隻有.NET 2.0支援泛型,你就不能夠使用泛型。

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

對泛型我應該使用什麼命名規範?

泛型最佳實踐C#
泛型最佳實踐C#

        我建議使用一個單獨的大寫字母來表示一般類型參數。如果你對類型參數沒有其他的跟上下文有關的資訊(additional contextual information),你應該使用字母T:

泛型最佳實踐C#
泛型最佳實踐C#

public class MyClass<T>

泛型最佳實踐C#

{

泛型最佳實踐C#

}

泛型最佳實踐C#
泛型最佳實踐C#

 在所有其他場合下,微軟正式的對泛型的的命名規範指導是:

泛型最佳實踐C#
泛型最佳實踐C#

一般類型參數要有描述性的名字,除非一個單獨的字母已經表示得很清楚,再增加描述性的名字也沒有多大用處。

泛型最佳實踐C#
泛型最佳實踐C#

public interface ISessionChannel<TSession> 

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

public delegate TOutput Converter<TInput,TOutput>(TInput from);

泛型最佳實踐C#

可以考慮在一般類型參數的名字中表示出添加給該一般類型參數的限制。例如,一個被限制到ISession接口的參數可以起名為TSession。

泛型最佳實踐C#

我應該在泛型接口上面添加限制嗎?

泛型最佳實踐C#
泛型最佳實踐C#

        接口可以為其使用的範型類型添加限制,例如:  

泛型最佳實踐C#
泛型最佳實踐C#

public interface ILinkedList<T> where T : IComparable<T>

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

  但是,你應該小心,在接口層面上定義限制還隐含有另外一層意思。為了強調接口與實作分離的思想,接口不應該包括任何一點實作的細節。雖然有很多方法可以用來實作範型接口,但是使用特定的類型參數畢竟是一種實作的細節。限制通常情況下會更加耦合(couple)接口和特定的實作。

泛型最佳實踐C#
泛型最佳實踐C#

        更好的方法是,為實作範型接口的類添加限制,保持接口本身沒有限制:

泛型最佳實踐C#
泛型最佳實踐C#

public class LinkedList<T> : ILinkedList<T> where T : IComparable<T>

泛型最佳實踐C#
泛型最佳實踐C#

   //Rest of the implementation  

泛型最佳實踐C#
泛型最佳實踐C#

如何處置(Dispose)泛型接口?

泛型最佳實踐C#
泛型最佳實踐C#

      在C#和Visual Basic中,如果你把一個一般類型參數的對象放在using語句中,編譯器無法知道用戶端(client)指定的實際類型是否支援IDisposable接口。是以編譯器不允許在using語句中使用一般類型參數的執行個體。

泛型最佳實踐C#
泛型最佳實踐C#

public class MyClass<T> 

泛型最佳實踐C#
泛型最佳實踐C#

   public void SomeMethod(T t)

泛型最佳實踐C#

   {

泛型最佳實踐C#

      using(t)//Does not compile 

泛型最佳實踐C#

      {

泛型最佳實踐C#
泛型最佳實踐C#

   }

泛型最佳實踐C#
泛型最佳實踐C#

當然,你可以強制限制類型參數支援IDisposable接口:

泛型最佳實踐C#
泛型最佳實踐C#

public class MyClass<T> where T : IDisposable 

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

      using(t)

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

     但是你不應該這麼做。這樣做的問題在于你不能使用接口作為類型參數了,即使這個接口的基礎類型(underlying type)支援IDisposable也不行:

泛型最佳實踐C#
泛型最佳實踐C#

public interface IMyInterface

泛型最佳實踐C#

{}

泛型最佳實踐C#

public class MyOtherClass : IMyInterface,IDisposable 

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

MyOtherClass myOtherClass = new MyOtherClass();

泛型最佳實踐C#

MyClass<IMyInterface> obj = new MyClass<IMyInterface>();//Does not compile

泛型最佳實踐C#

obj.SomeMethod(myOtherClass); 

泛型最佳實踐C#

  作為替代,我建議你在using語句裡對一般類型參數使用C#中的as操作符或者Visual Basic中的TryCast操作符來允許接口作為一般類型參數使用:

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

      using(t as IDisposable)

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

可以對一般類型參數進行類型轉換嗎?

泛型最佳實踐C#
泛型最佳實踐C#

        對于隐式轉換,編譯器隻允許将一般類型參數轉換為object類型,或者其限制裡指定的那個類型:

泛型最佳實踐C#
泛型最佳實踐C#

interface ISomeInterface

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

class BaseClass

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

class MyClass<T> where T : BaseClass,ISomeInterface

泛型最佳實踐C#
泛型最佳實踐C#

   void SomeMethod(T t)

泛型最佳實踐C#
泛型最佳實踐C#

      ISomeInterface obj1 = t;

泛型最佳實踐C#

      BaseClass      obj2 = t;

泛型最佳實踐C#

      object         obj3 = t;

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

這種隐式轉換當然是類型安全的,因為無效的轉換在編譯時就會被發現。

泛型最佳實踐C#
泛型最佳實踐C#

        對于顯示轉換,編譯器允許将一般類型參數轉換到任何接口,但是不能轉換為類:

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

class SomeClass

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

class MyClass<T> 

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

      ISomeInterface obj1 = (ISomeInterface)t;//Compiles

泛型最佳實踐C#

      SomeClass      obj2 = (SomeClass)t;     //Does not compile

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

 但是,你可以通過使用一個臨時的object類型變量來強制将一般類型參數轉到到任何其他類型:

泛型最佳實踐C#
泛型最佳實踐C#

class MyOtherClass

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

      object temp = t;

泛型最佳實踐C#

      MyOtherClass obj = (MyOtherClass)temp;

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

毫無疑問,這樣的顯示轉換是很危險的,因為如果實際使用的替代一般類型參數的類型不是從你要轉換到的類型那裡繼承的話,就可能在運作時抛出異常。

泛型最佳實踐C#
泛型最佳實踐C#

        為了避免這種轉換時有異常的風險,一個更好的辦法是使用is或者as操作符。如果一般類型參數是(is)要查詢的類型,is 操作符會傳回true,而as操作符會在兩個類型相容的時候執行轉換,否則将傳回null。

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

      if(t is int)

泛型最佳實踐C#
泛型最佳實踐C#

泛型最佳實踐C#
泛型最佳實踐C#

      if(t is LinkedList<int,string>)

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

      string str = t as string;

泛型最佳實踐C#

      if(str != null)

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

      LinkedList<int,string> list = t as LinkedList<int,string>;

泛型最佳實踐C#

      if(list != null)

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

對泛型類如何同步多線程通路?

泛型最佳實踐C#
泛型最佳實踐C#

        通常來說,你不應該在一般類型參數上應用Monitor。這是因為Monitor隻能用于引用類型。當你使用範型的時候,編譯器不能預先判斷你将會提供一個引用類型還是值類型的類型參數。在C#中,編譯器會允許你使用lock語句,但是如果你提供了一個值類型作為類型參數,lock語句在運作時将不起作用。在Visual Basic中,編譯器如果不能确定一般類型參數是一個引用類型,它将不允許在一般類型參數上面使用SyncLock。

泛型最佳實踐C#
泛型最佳實踐C#

        在C#和Visual Basic中,唯一你可以安全地将一般類型參數鎖住的時候,是你将一般類型參數限制為引用類型,要麼添加限制使其為引用類型,要麼從一個基類中繼承:

泛型最佳實踐C#
泛型最佳實踐C#

public class MyClass<T> where T : class

泛型最佳實踐C#

{..}

泛型最佳實踐C#
泛型最佳實踐C#

public class SomeClass

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

public class MyClass<T> where T : SomeClass

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

     然而,通常對于同步來說,最好避免部分地鎖住單獨的成員變量,因為這會增加死鎖的可能性。

泛型最佳實踐C#
泛型最佳實踐C#

如何序列化泛型類?

泛型最佳實踐C#
泛型最佳實踐C#

        包括了一般類型參數作為成員的範型類是可以被标記為序列化的:

泛型最佳實踐C#
泛型最佳實踐C#

[Serializable]

泛型最佳實踐C#

public class MySerializableClass<T>

泛型最佳實踐C#
泛型最佳實踐C#

   T m_T;

泛型最佳實踐C#
泛型最佳實踐C#

  但是,在這種情況下,隻有指定的類型參數可以被序列化時,範型類才可以被序列化。看下面的代碼:

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

MySerializableClass<SomeClass> obj;

泛型最佳實踐C#

  obj不能被序列化,因為類型參數SomeClass不可以被序列化。是以,MySerializableClass<T>可能可以,也可能不可以被序列化,取決于使用的一般類型參數。這樣可能導緻在運作時丢失資料或者系統崩潰,因為客戶應用程式可能不能夠保持對象的狀态。

泛型最佳實踐C#
泛型最佳實踐C#

        目前,.NET沒有提供将一般類型參數限制為可序列化的機制。解決辦法是在運作時在使用這個類型之前單獨進行檢查,并且在任何損害發生之前馬上中止使用。你可以把這個運作時的驗證放在靜态構造器裡面:

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

class MySerializableClass<T>

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

   static MySerializableClass()   

泛型最佳實踐C#
泛型最佳實踐C#

      ConstrainType(typeof(T));

泛型最佳實踐C#
泛型最佳實踐C#

   static void ConstrainType(Type type)

泛型最佳實踐C#
泛型最佳實踐C#

      bool serializable = type.IsSerializable;

泛型最佳實踐C#

      if(serializable == false)

泛型最佳實踐C#
泛型最佳實踐C#

         string message = "The type " + type + " is not serializable";

泛型最佳實踐C#

         throw new InvalidOperationException(message);

泛型最佳實踐C#

      }

泛型最佳實踐C#
泛型最佳實踐C#
泛型最佳實踐C#

 靜态構造器對每一個應用程式域的每一個類型隻執行一次,而且是在類型第一次被請求執行個體化之前。盡管你有一些通過程式設計的方式來在運作時進行判斷和執行檢查,但是這種在靜态構造器裡面執行限制驗證的技術,對任何無法在編譯時進行檢查的限制都适用。

本文轉自高海東部落格園部落格,原文連結:http://www.cnblogs.com/ghd258/archive/2005/11/07/270411.html,如需轉載請自行聯系原作者

繼續閱讀