泛型最佳实践C#

简介:

什么时候我不应该使用泛型?对泛型我应该使用什么命名规范?我应该在泛型接口上面添加约束吗?
如何处置(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 
{
   
public void SomeMethod(T t)
   
{
      
using(t)
      
{}
   }

}

     但是你不应该这么做。这样做的问题在于你不能使用接口作为类型参数了,即使这个接口的基础类型(underlying type)支持IDisposable也不行:

public   interface  IMyInterface
{}
public   class  MyOtherClass : IMyInterface,IDisposable 
{}
public   class  MyClass < T >  where T : IDisposable 
{
   
public void SomeMethod(T t)
   
{
      
using(t)
      
{}
   }

}

MyOtherClass myOtherClass 
=   new  MyOtherClass();
MyClass
< IMyInterface >  obj  =   new  MyClass < IMyInterface > (); // Does not compile
obj.SomeMethod(myOtherClass); 
  作为替代,我建议你在using语句里对一般类型参数使用C#中的as操作符或者Visual Basic中的TryCast操作符来允许接口作为一般类型参数使用:


public   class  MyClass < T >  
{
   
public void SomeMethod(T t)
   
{
      
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;
   }

}

这种隐式转换当然是类型安全的,因为无效的转换在编译时就会被发现。

        对于显示转换,编译器允许将一般类型参数转换到任何接口,但是不能转换为类:

interface  ISomeInterface
{}
class  SomeClass
{}
class  MyClass < T >  
{
   
void SomeMethod(T t)
   
{
      ISomeInterface obj1 
= (ISomeInterface)t;//Compiles
      SomeClass      obj2 = (SomeClass)t;     //Does not compile
   }

}

 但是,你可以通过使用一个临时的object类型变量来强制将一般类型参数转到到任何其他类型:

class  MyOtherClass
{}

class  MyClass < T >  
{
  
   
void SomeMethod(T t)
   
   
{
      
object temp = t;
      MyOtherClass obj 
= (MyOtherClass)temp;
   
   }

}

毫无疑问,这样的显示转换是很危险的,因为如果实际使用的替代一般类型参数的类型不是从你要转换到的类型那里继承的话,就可能在运行时抛出异常。

        为了避免这种转换时有异常的风险,一个更好的办法是使用is或者as操作符。如果一般类型参数是(
is )要查询的类型, is  操作符会返回true,而as操作符会在两个类型兼容的时候执行转换,否则将返回null。

public   class  MyClass < T >  
{
   
public void SomeMethod(T t)
   
{
      
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;
}

  但是,在这种情况下,只有指定的类型参数可以被序列化时,范型类才可以被序列化。看下面的代码:


public   class  SomeClass
{}
MySerializableClass
< SomeClass >  obj;
  obj不能被序列化,因为类型参数SomeClass不可以被序列化。因此,MySerializableClass
< T > 可能可以,也可能不可以被序列化,取决于使用的一般类型参数。这样可能导致在运行时丢失数据或者系统崩溃,因为客户应用程序可能不能够保持对象的状态。

        目前,.NET没有提供将一般类型参数约束为可序列化的机制。解决办法是在运行时在使用这个类型之前单独进行检查,并且在任何损害发生之前马上中止使用。你可以把这个运行时的验证放在静态构造器里面:

[Serializable]
class  MySerializableClass < T >
{
   T m_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,如需转载请自行联系原作者
相关文章
|
存储 安全 编译器
C# 11.0中的泛型属性:类型安全的新篇章
【1月更文挑战第23天】C# 11.0引入了泛型属性的概念,这一新特性为开发者提供了更高级别的类型安全性和灵活性。本文将详细探讨C# 11.0中泛型属性的工作原理、使用场景以及它们对现有编程模式的改进。通过深入了解泛型属性,开发者将能够编写更加健壮、可维护的代码,并充分利用C#语言的最新发展。
|
存储 算法 安全
C#三十二 泛型的理解和使用
C#三十二 泛型的理解和使用
186 0
|
安全 程序员 编译器
C#一分钟浅谈:泛型编程基础
在现代软件开发中,泛型编程是一项关键技能,它使开发者能够编写类型安全且可重用的代码。C# 自 2.0 版本起支持泛型编程,本文将从基础概念入手,逐步深入探讨 C# 中的泛型,并通过具体实例帮助理解常见问题及其解决方法。泛型通过类型参数替代具体类型,提高了代码复用性和类型安全性,减少了运行时性能开销。文章详细介绍了如何定义泛型类和方法,并讨论了常见的易错点及解决方案,帮助读者更好地掌握这一技术。
395 11
|
编译器 C#
C#中内置的泛型委托Func与Action
C#中内置的泛型委托Func与Action
575 4
|
存储 安全 Java
34.C#:listT泛型集合
34.C#:listT泛型集合
251 1
|
开发框架 安全 .NET
C# .NET面试系列三:集合、异常、泛型、LINQ、委托、EF!
<h2>集合、异常、泛型、LINQ、委托、EF! #### 1. IList 接口与 List 的区别是什么? IList 接口和 List 类是C#中集合的两个相关但不同的概念。下面是它们的主要区别: <b>IList 接口</b> IList 接口是C#中定义的一个泛型接口,位于 System.Collections 命名空间。它派生自 ICollection 接口,定义了一个可以通过索引访问的有序集合。 ```c# IList 接口包含一系列索引化的属性和方法,允许按索引访问、插入、移除元素等。 由于是接口,它只定义了成员的契约,而不提供具体的实现。类似于 IEnumera
894 2
|
存储 安全 算法
C# 泛型:类型参数化的强大工具
【1月更文挑战第7天】本文将深入探讨C#语言中的泛型编程,包括泛型的定义、用途、优势以及实际应用。通过类型参数化,泛型允许开发者编写更加灵活且可重用的代码,同时提高程序的类型安全性和性能。本文将通过示例代码和详细解释,帮助读者更好地理解泛型在C#中的重要性和实用性。
|
存储 Java 编译器
【从Java转C#】第五章:泛型
【从Java转C#】第五章:泛型
|
机器学习/深度学习 存储 缓存
一文带你搞懂C#泛型
泛型是.net 2.0中提供的新特性,是框架的一种升级,用于处理用一个事物来代替多种不同需求的情况。下面我们就一块来看一下具体的讲解吧。
|
安全 C#
c# 泛型约束
c# 泛型约束
260 0