什麼時候我不應該使用泛型?對泛型我應該使用什麼命名規範?我應該在泛型接口上面添加限制嗎?
如何處置(Dispose)泛型接口?可以對一般類型參數進行類型轉換嗎?
對泛型類如何同步多線程通路?如何序列化泛型類?
什麼時候我不應該使用泛型?
不使用泛型的主要原因就是跨目标(cross-targeting)——如果你要在.NET 1.1和.NET 2.0下編譯相同的代碼,那麼由于隻有.NET 2.0支援泛型,你就不能夠使用泛型。
對泛型我應該使用什麼命名規範?
我建議使用一個單獨的大寫字母來表示一般類型參數。如果你對類型參數沒有其他的跟上下文有關的資訊(additional contextual information),你應該使用字母T:
public class MyClass<T>
{
}
在所有其他場合下,微軟正式的對泛型的的命名規範指導是:
一般類型參數要有描述性的名字,除非一個單獨的字母已經表示得很清楚,再增加描述性的名字也沒有多大用處。
public interface ISessionChannel<TSession>
public delegate TOutput Converter<TInput,TOutput>(TInput from);
可以考慮在一般類型參數的名字中表示出添加給該一般類型參數的限制。例如,一個被限制到ISession接口的參數可以起名為TSession。
我應該在泛型接口上面添加限制嗎?
接口可以為其使用的範型類型添加限制,例如:
public interface ILinkedList<T> where T : IComparable<T>
但是,你應該小心,在接口層面上定義限制還隐含有另外一層意思。為了強調接口與實作分離的思想,接口不應該包括任何一點實作的細節。雖然有很多方法可以用來實作範型接口,但是使用特定的類型參數畢竟是一種實作的細節。限制通常情況下會更加耦合(couple)接口和特定的實作。
更好的方法是,為實作範型接口的類添加限制,保持接口本身沒有限制:
public class LinkedList<T> : ILinkedList<T> where T : IComparable<T>
//Rest of the implementation
如何處置(Dispose)泛型接口?
在C#和Visual Basic中,如果你把一個一般類型參數的對象放在using語句中,編譯器無法知道用戶端(client)指定的實際類型是否支援IDisposable接口。是以編譯器不允許在using語句中使用一般類型參數的執行個體。
public class MyClass<T>
public void SomeMethod(T t)
{
using(t)//Does not compile
{
}
當然,你可以強制限制類型參數支援IDisposable接口:
public class MyClass<T> where T : IDisposable
using(t)
但是你不應該這麼做。這樣做的問題在于你不能使用接口作為類型參數了,即使這個接口的基礎類型(underlying type)支援IDisposable也不行:
public interface IMyInterface
{}
public class MyOtherClass : IMyInterface,IDisposable
MyOtherClass myOtherClass = new MyOtherClass();
MyClass<IMyInterface> obj = new MyClass<IMyInterface>();//Does not compile
obj.SomeMethod(myOtherClass);
作為替代,我建議你在using語句裡對一般類型參數使用C#中的as操作符或者Visual Basic中的TryCast操作符來允許接口作為一般類型參數使用:
using(t as IDisposable)
可以對一般類型參數進行類型轉換嗎?
對于隐式轉換,編譯器隻允許将一般類型參數轉換為object類型,或者其限制裡指定的那個類型:
interface ISomeInterface
class BaseClass
class MyClass<T> where T : BaseClass,ISomeInterface
void SomeMethod(T t)
ISomeInterface obj1 = t;
BaseClass obj2 = t;
object obj3 = t;
這種隐式轉換當然是類型安全的,因為無效的轉換在編譯時就會被發現。
對于顯示轉換,編譯器允許将一般類型參數轉換到任何接口,但是不能轉換為類:
class SomeClass
class MyClass<T>
ISomeInterface obj1 = (ISomeInterface)t;//Compiles
SomeClass obj2 = (SomeClass)t; //Does not compile
但是,你可以通過使用一個臨時的object類型變量來強制将一般類型參數轉到到任何其他類型:
class MyOtherClass
object temp = t;
MyOtherClass obj = (MyOtherClass)temp;
毫無疑問,這樣的顯示轉換是很危險的,因為如果實際使用的替代一般類型參數的類型不是從你要轉換到的類型那裡繼承的話,就可能在運作時抛出異常。
為了避免這種轉換時有異常的風險,一個更好的辦法是使用is或者as操作符。如果一般類型參數是(is)要查詢的類型,is 操作符會傳回true,而as操作符會在兩個類型相容的時候執行轉換,否則将傳回null。
if(t is int)
}
if(t is LinkedList<int,string>)
string str = t as string;
if(str != null)
LinkedList<int,string> list = t as LinkedList<int,string>;
if(list != null)
對泛型類如何同步多線程通路?
通常來說,你不應該在一般類型參數上應用Monitor。這是因為Monitor隻能用于引用類型。當你使用範型的時候,編譯器不能預先判斷你将會提供一個引用類型還是值類型的類型參數。在C#中,編譯器會允許你使用lock語句,但是如果你提供了一個值類型作為類型參數,lock語句在運作時将不起作用。在Visual Basic中,編譯器如果不能确定一般類型參數是一個引用類型,它将不允許在一般類型參數上面使用SyncLock。
在C#和Visual Basic中,唯一你可以安全地将一般類型參數鎖住的時候,是你将一般類型參數限制為引用類型,要麼添加限制使其為引用類型,要麼從一個基類中繼承:
public class MyClass<T> where T : class
{..}
public class SomeClass
public class MyClass<T> where T : SomeClass
然而,通常對于同步來說,最好避免部分地鎖住單獨的成員變量,因為這會增加死鎖的可能性。
如何序列化泛型類?
包括了一般類型參數作為成員的範型類是可以被标記為序列化的:
[Serializable]
public class MySerializableClass<T>
T m_T;
但是,在這種情況下,隻有指定的類型參數可以被序列化時,範型類才可以被序列化。看下面的代碼:
MySerializableClass<SomeClass> obj;
obj不能被序列化,因為類型參數SomeClass不可以被序列化。是以,MySerializableClass<T>可能可以,也可能不可以被序列化,取決于使用的一般類型參數。這樣可能導緻在運作時丢失資料或者系統崩潰,因為客戶應用程式可能不能夠保持對象的狀态。
目前,.NET沒有提供将一般類型參數限制為可序列化的機制。解決辦法是在運作時在使用這個類型之前單獨進行檢查,并且在任何損害發生之前馬上中止使用。你可以把這個運作時的驗證放在靜态構造器裡面:
class MySerializableClass<T>
static MySerializableClass()
ConstrainType(typeof(T));
static void ConstrainType(Type type)
bool serializable = type.IsSerializable;
if(serializable == false)
string message = "The type " + type + " is not serializable";
throw new InvalidOperationException(message);
}
靜态構造器對每一個應用程式域的每一個類型隻執行一次,而且是在類型第一次被請求執行個體化之前。盡管你有一些通過程式設計的方式來在運作時進行判斷和執行檢查,但是這種在靜态構造器裡面執行限制驗證的技術,對任何無法在編譯時進行檢查的限制都适用。
本文轉自高海東部落格園部落格,原文連結:http://www.cnblogs.com/ghd258/archive/2005/11/07/270411.html,如需轉載請自行聯系原作者