泛型最佳实践C#

简介:

None.gif 什么时候我不应该使用泛型?对泛型我应该使用什么命名规范?我应该在泛型接口上面添加约束吗?
None.gif如何处置(Dispose)泛型接口?可以对一般类型参数进行类型转换吗?
None.gif对泛型类如何同步多线程访问?如何序列化泛型类?
None.gif
None.gif什么时候我不应该使用泛型?
None.gif
None.gif        不使用泛型的主要原因就是跨目标(cross
- targeting)——如果你要在.NET  1 .1和.NET  2 .0下编译相同的代码,那么由于只有.NET  2 .0支持泛型,你就不能够使用泛型。
None.gif
None.gif
None.gif对泛型我应该使用什么命名规范?
None.gif
None.gif        我建议使用一个单独的大写字母来表示一般类型参数。如果你对类型参数没有其他的跟上下文有关的信息(additional contextual information),你应该使用字母T:
None.gif
None.gif
public   class  MyClass < T >
ExpandedBlockStart.gif
{dot.gif}
None.gif      
None.gif 在所有其他场合下,微软正式的对泛型的的命名规范指导是:
None.gif
None.gif一般类型参数要有描述性的名字,除非一个单独的字母已经表示得很清楚,再增加描述性的名字也没有多大用处。
None.gif
None.gif
public   interface  ISessionChannel < TSession >  
ExpandedBlockStart.gif
{dot.gif}
None.gif
public   delegate  TOutput Converter < TInput,TOutput > (TInput from);
None.gif可以考虑在一般类型参数的名字中表示出添加给该一般类型参数的约束。例如,一个被约束到ISession接口的参数可以起名为TSession。
None.gif我应该在泛型接口上面添加约束吗?
None.gif
None.gif        接口可以为其使用的范型类型添加约束,例如:  
None.gif
None.gif
public   interface  ILinkedList < T >  where T : IComparable < T >
ExpandedBlockStart.gif
{dot.gif}
None.gif  但是,你应该小心,在接口层面上定义约束还隐含有另外一层意思。为了强调接口与实现分离的思想,接口不应该包括任何一点实现的细节。虽然有很多方法可以用来实现范型接口,但是使用特定的类型参数毕竟是一种实现的细节。约束通常情况下会更加耦合(couple)接口和特定的实现。
None.gif
None.gif        更好的方法是,为实现范型接口的类添加约束,保持接口本身没有约束:
None.gif
None.gif
public   class  LinkedList < T >  : ILinkedList < T >  where T : IComparable < T >
ExpandedBlockStart.gif
{
InBlock.gif   
//Rest of the implementation  
ExpandedBlockEnd.gif
}

None.gif如何处置(Dispose)泛型接口?
None.gif
None.gif      在C#和Visual Basic中,如果你把一个一般类型参数的对象放在using语句中,编译器无法知道客户端(client)指定的实际类型是否支持IDisposable接口。因此编译器不允许在using语句中使用一般类型参数的实例。
None.gif
None.gif
public   class  MyClass < T >  
ExpandedBlockStart.gif
{
InBlock.gif   
public void SomeMethod(T t)
ExpandedSubBlockStart.gif   
{
InBlock.gif      
using(t)//Does not compile 
ExpandedSubBlockStart.gif
      {dot.gif}
ExpandedSubBlockEnd.gif   }

ExpandedBlockEnd.gif}

None.gif当然,你可以强制约束类型参数支持IDisposable接口:
None.gif
None.gif
public   class  MyClass < T >  where T : IDisposable 
ExpandedBlockStart.gif
{
InBlock.gif   
public void SomeMethod(T t)
ExpandedSubBlockStart.gif   
{
InBlock.gif      
using(t)
ExpandedSubBlockStart.gif      
{dot.gif}
ExpandedSubBlockEnd.gif   }

ExpandedBlockEnd.gif}

None.gif     但是你不应该这么做。这样做的问题在于你不能使用接口作为类型参数了,即使这个接口的基础类型(underlying type)支持IDisposable也不行:
None.gif
None.gif
public   interface  IMyInterface
ExpandedBlockStart.gif
{}
None.gif
public   class  MyOtherClass : IMyInterface,IDisposable 
ExpandedBlockStart.gif
{dot.gif}
None.gif
public   class  MyClass < T >  where T : IDisposable 
ExpandedBlockStart.gif
{
InBlock.gif   
public void SomeMethod(T t)
ExpandedSubBlockStart.gif   
{
InBlock.gif      
using(t)
ExpandedSubBlockStart.gif      
{dot.gif}
ExpandedSubBlockEnd.gif   }

ExpandedBlockEnd.gif}

None.gifMyOtherClass myOtherClass 
=   new  MyOtherClass();
None.gifMyClass
< IMyInterface >  obj  =   new  MyClass < IMyInterface > (); // Does not compile
None.gif
obj.SomeMethod(myOtherClass); 
None.gif  作为替代,我建议你在using语句里对一般类型参数使用C#中的as操作符或者Visual Basic中的TryCast操作符来允许接口作为一般类型参数使用:
None.gif
None.gif
None.gif
public   class  MyClass < T >  
ExpandedBlockStart.gif
{
InBlock.gif   
public void SomeMethod(T t)
ExpandedSubBlockStart.gif   
{
InBlock.gif      
using(t as IDisposable)
ExpandedSubBlockStart.gif      
{dot.gif}
ExpandedSubBlockEnd.gif   }

ExpandedBlockEnd.gif}

None.gif可以对一般类型参数进行类型转换吗?
None.gif
None.gif        对于隐式转换,编译器只允许将一般类型参数转换为object类型,或者其约束里指定的那个类型:
None.gif
None.gif
interface  ISomeInterface
ExpandedBlockStart.gif
{dot.gif}
None.gif
class  BaseClass
ExpandedBlockStart.gif
{dot.gif}
None.gif
class  MyClass < T >  where T : BaseClass,ISomeInterface
ExpandedBlockStart.gif
{
InBlock.gif   
void SomeMethod(T t)
ExpandedSubBlockStart.gif   
{
InBlock.gif      ISomeInterface obj1 
= t;
InBlock.gif      BaseClass      obj2 
= t;
InBlock.gif      
object         obj3 = t;
ExpandedSubBlockEnd.gif   }

ExpandedBlockEnd.gif}

None.gif这种隐式转换当然是类型安全的,因为无效的转换在编译时就会被发现。
None.gif
None.gif        对于显示转换,编译器允许将一般类型参数转换到任何接口,但是不能转换为类:
None.gif
None.gif
interface  ISomeInterface
ExpandedBlockStart.gif
{dot.gif}
None.gif
class  SomeClass
ExpandedBlockStart.gif
{dot.gif}
None.gif
class  MyClass < T >  
ExpandedBlockStart.gif
{
InBlock.gif   
void SomeMethod(T t)
ExpandedSubBlockStart.gif   
{
InBlock.gif      ISomeInterface obj1 
= (ISomeInterface)t;//Compiles
InBlock.gif
      SomeClass      obj2 = (SomeClass)t;     //Does not compile
ExpandedSubBlockEnd.gif
   }

ExpandedBlockEnd.gif}

None.gif 但是,你可以通过使用一个临时的object类型变量来强制将一般类型参数转到到任何其他类型:
None.gif
None.gif
class  MyOtherClass
ExpandedBlockStart.gif
{dot.gif}
None.gif
None.gif
class  MyClass < T >  
ExpandedBlockStart.gif
{
InBlock.gif  
InBlock.gif   
void SomeMethod(T t)
InBlock.gif   
ExpandedSubBlockStart.gif   
{
InBlock.gif      
object temp = t;
InBlock.gif      MyOtherClass obj 
= (MyOtherClass)temp;
InBlock.gif   
ExpandedSubBlockEnd.gif   }

ExpandedBlockEnd.gif}

None.gif毫无疑问,这样的显示转换是很危险的,因为如果实际使用的替代一般类型参数的类型不是从你要转换到的类型那里继承的话,就可能在运行时抛出异常。
None.gif
None.gif        为了避免这种转换时有异常的风险,一个更好的办法是使用is或者as操作符。如果一般类型参数是(
is )要查询的类型, is  操作符会返回true,而as操作符会在两个类型兼容的时候执行转换,否则将返回null。
None.gif
None.gif
public   class  MyClass < T >  
ExpandedBlockStart.gif
{
InBlock.gif   
public void SomeMethod(T t)
ExpandedSubBlockStart.gif   
{
InBlock.gif      
if(t is int)
ExpandedSubBlockStart.gif      
{dot.gif} 
InBlock.gif
InBlock.gif      
if(t is LinkedList<int,string>)
ExpandedSubBlockStart.gif      
{dot.gif}
InBlock.gif
InBlock.gif      
string str = t as string;
InBlock.gif      
if(str != null)
ExpandedSubBlockStart.gif      
{dot.gif}
InBlock.gif
InBlock.gif      LinkedList
<int,string> list = t as LinkedList<int,string>;
InBlock.gif      
if(list != null)
ExpandedSubBlockStart.gif      
{dot.gif}
ExpandedSubBlockEnd.gif   }

ExpandedBlockEnd.gif}

None.gif
None.gif
None.gif
None.gif对泛型类如何同步多线程访问?
None.gif
None.gif        通常来说,你不应该在一般类型参数上应用Monitor。这是因为Monitor只能用于引用类型。当你使用范型的时候,编译器不能预先判断你将会提供一个引用类型还是值类型的类型参数。在C#中,编译器会允许你使用lock语句,但是如果你提供了一个值类型作为类型参数,lock语句在运行时将不起作用。在Visual Basic中,编译器如果不能确定一般类型参数是一个引用类型,它将不允许在一般类型参数上面使用SyncLock。
None.gif
None.gif        在C#和Visual Basic中,唯一你可以安全地将一般类型参数锁住的时候,是你将一般类型参数限制为引用类型,要么添加约束使其为引用类型,要么从一个基类中继承:
None.gif
None.gif
public   class  MyClass < T >  where T :  class
ExpandedBlockStart.gif
{..}
None.gif
None.gif
public   class  SomeClass
ExpandedBlockStart.gif
{dot.gif}
None.gif
public   class  MyClass < T >  where T : SomeClass
ExpandedBlockStart.gif
{dot.gif}
None.gif     然而,通常对于同步来说,最好避免部分地锁住单独的成员变量,因为这会增加死锁的可能性。
None.gif
None.gif如何序列化泛型类?
None.gif
None.gif        包括了一般类型参数作为成员的范型类是可以被标记为序列化的:
None.gif
None.gif[Serializable]
None.gif
public   class  MySerializableClass < T >
ExpandedBlockStart.gif
{
InBlock.gif   T m_T;
ExpandedBlockEnd.gif}

None.gif  但是,在这种情况下,只有指定的类型参数可以被序列化时,范型类才可以被序列化。看下面的代码:
None.gif
None.gif
None.gif
public   class  SomeClass
ExpandedBlockStart.gif
{}
None.gifMySerializableClass
< SomeClass >  obj;
None.gif  obj不能被序列化,因为类型参数SomeClass不可以被序列化。因此,MySerializableClass
< T > 可能可以,也可能不可以被序列化,取决于使用的一般类型参数。这样可能导致在运行时丢失数据或者系统崩溃,因为客户应用程序可能不能够保持对象的状态。
None.gif
None.gif        目前,.NET没有提供将一般类型参数约束为可序列化的机制。解决办法是在运行时在使用这个类型之前单独进行检查,并且在任何损害发生之前马上中止使用。你可以把这个运行时的验证放在静态构造器里面:
None.gif
None.gif[Serializable]
None.gif
class  MySerializableClass < T >
ExpandedBlockStart.gif
{
InBlock.gif   T m_T;
InBlock.gif
InBlock.gif   
static MySerializableClass()   
ExpandedSubBlockStart.gif   
{
InBlock.gif      ConstrainType(
typeof(T));
ExpandedSubBlockEnd.gif   }

InBlock.gif   
static void ConstrainType(Type type)
ExpandedSubBlockStart.gif   
{
InBlock.gif      
bool serializable = type.IsSerializable;
InBlock.gif      
if(serializable == false)
ExpandedSubBlockStart.gif      
{
InBlock.gif         
string message = "The type " + type + " is not serializable";
InBlock.gif         
throw new InvalidOperationException(message);
ExpandedSubBlockEnd.gif      }

ExpandedSubBlockEnd.gif   }

ExpandedBlockEnd.gif}

None.gif 静态构造器对每一个应用程序域的每一个类型只执行一次,而且是在类型第一次被请求实例化之前。尽管你有一些通过编程的方式来在运行时进行判断和执行检查,但是这种在静态构造器里面执行约束验证的技术,对任何无法在编译时进行检查的约束都适用。




本文转自高海东博客园博客,原文链接:http://www.cnblogs.com/ghd258/archive/2005/11/07/270411.html,如需转载请自行联系原作者
相关文章
|
3月前
|
存储 安全 编译器
C# 11.0中的泛型属性:类型安全的新篇章
【1月更文挑战第23天】C# 11.0引入了泛型属性的概念,这一新特性为开发者提供了更高级别的类型安全性和灵活性。本文将详细探讨C# 11.0中泛型属性的工作原理、使用场景以及它们对现有编程模式的改进。通过深入了解泛型属性,开发者将能够编写更加健壮、可维护的代码,并充分利用C#语言的最新发展。
|
3月前
|
存储 JavaScript Java
Java编程基础 - 泛型
Java编程基础 - 泛型
20 0
|
9月前
|
安全 Java
什么是Java泛型?主要应用场景有哪些?
什么是Java泛型?主要应用场景有哪些?
133 0
|
6月前
|
Swift C++ 索引
33 Swift泛型的发展历史与泛型的概述
Swift泛型的发展历史与泛型的概述
35 0
|
8月前
|
存储 JavaScript 安全
TypeScript中的泛型:深入理解泛型的概念和应用场景
TypeScript中的泛型:深入理解泛型的概念和应用场景
|
9月前
|
存储 安全 Java
Java泛型详解:为什么使用泛型?如何使用泛型?
Java泛型详解:为什么使用泛型?如何使用泛型?
116 0
泛型的高级运用,代码重构必须要了解的技巧
前言 泛型相信大家都不陌生,经常都会用到,像在一些集合类啊,一些开源框架啊,这种东西随处可见,如果不能好好理解的话,看起源码来也会增加了一点儿复杂度。 泛型的好处,扩展性强,低耦合业务内容,大幅度的减少重复代码。 本篇文章,基于对泛型有一定了解,想更进一步运用的朋友。
C#编程-102:泛型集合类概述
C#编程-102:泛型集合类概述
C#编程-102:泛型集合类概述
DHL
|
存储 算法 安全
90%的人都不懂的泛型,泛型的缺陷和应用场景
Kotlin 和 Java 的协变和逆变的区别和应用场景,数组协变的缺陷,Kotlin 和 Java 数组协变的不同之处
DHL
263 0
90%的人都不懂的泛型,泛型的缺陷和应用场景
|
并行计算 Java 编译器
Lambda表达式最佳实践
Lambda表达式最佳实践

热门文章

最新文章