曾经有人问我这样一个问题:如何迫使子类提供无参构造函数。当时给出的答案是让子类实现这样一个接口。
public interface
IMustHaveParameterLessConstructor
where T :
IMustHaveParameterLessConstructor,
new()
{
}
这种在泛型参数中引用自身的技法,还有个名字,叫做“Self-Referencing
Generics”模式。这个技法在C++中已经被使用了20多年,只不过叫做Curiously Recurring
Template。
这个技法可以用来实现不少有用的功能。比如为所有子类实现Singleton模式
public class
Singleton
where T : new()
{
private static readonly T instance =
new T();
public static T Instance
{
get { return instance;
}
}
}
public class Model :
Singleton
{
}
下面谈谈这个技法的劣势。
首先,它影响代码的可读性,比如说用到这种程度的时候。
public
interface IComponent
{ }
public interface
IComponentProvider
where TComponent : IComponent>
{ }
public
interface IComponentProviderWorkaround
where TComponent :
IComponent
where TSelf : IComponentProviderWorkaround
{
}
这就是自找麻烦了。别人读起来也会想骂人。
其次,这个技法其实是反面向对象的。如果你的类继承层次多于一层,就会产生问题。
Eric
Lippert在其博文《Curiouser and
curiouser》中从继承关系的逻辑合理性的角度进行了分析。本质上讲,自引用泛型违反了里氏替换原则。下面节选了一些要点。
It seems like
an abuse of a mechanism rather than the modeling of a concept from the program‘s
"business domain"
……
My advice is to think very hard before you
implement this sort of curious pattern in C#; do the benefits to the customer
really outweigh the costs associated with the mental burden you‘re placing on
the code maintainers?托福答案
Eric文中的例子还是很温和的,至少没有导致什么编译错误或是警告。于是就被一些人无视了。
那么我来写个能出编译错误的例子。
public
interface SoapArgs
where T : SoapArgs
{
}
public class
GenericSoapArgs : SoapArgs>
{
}
public class
DerivedGenericSoapArgs :
GenericSoapArgs
{
}
这三个类(或接口)的关系很一目了然对吧。又有这样一个函数,负责把SoapArgs发出去。
public
class SoapSender
{
public virtual void SendSoapArgs(T args)
where
T :
SoapArgs
{
}
}
也很简单对吧?逻辑上,这个函数可以接受前面两个类的实例对吧?可实际上,下面第二行代码会出编译错误。
new
SoapSender().SendSoapArgs(new GenericSoapArgs());
new
SoapSender().SendSoapArgs(new DerivedGenericSoapArgs());
错误信息是:
The
type ‘DerivedGenericSoapArgs‘ cannot be used as type parameter ‘T‘ in the
generic type or method ‘SoapSender.SendSoapArgs(T)‘. There is no implicit
reference conversion from ‘DerivedGenericSoapArgs‘ to ‘SoapArgs<
DerivedGenericSoapArgs>‘.
解决办法倒也算简单,让DerivedGenericSoapArgs自己再实现一遍SoapArgs接口就可以了——尽管它的父类已经实现了托福答案
原文:http://www.cnblogs.com/tianchaoy/p/3679118.html