德甲

自引用泛型模式分析奢侈品市场和消费

2020-02-15 18:53:22来源:励志吧0次阅读

曾经有人问我这样一个问题:如何迫使子类提供无参构造函数。当时给出的答案是让子类实现这样一个接口。

public interface IMustHaveParameterLessConstructorT where T : IMustHaveParameterLessConstructorT, new() { }这类在泛型参数中援用本身的技法,还有个名字,叫做Self-Referencing Generics模式。这个技法在C++中已被使用了20多年,只不过叫做Curiously Recurring Template。

这个技法可以用来实现很多有用的功能。比如为所有子类实现Singleton模式

public class SingletonT where T : new() { private static readonly T instance = new T(); public static T Instance { get { return instance; } } } public class Model : SingletonModel { }

下面谈谈这个技法的劣势。

首先,它影响代码的可读性,比如说用到这种程度的时候。

public interface IComponentT { } public interface IComponentProviderTComponent where TComponent : IComponentIComponentProviderTComponent { } public interface IComponentProviderWorkaroundTComponent, TSelf where TComponent : IComponentTSelf where TSelf : IComponentProviderWorkaroundTComponent, TSelf { }

这就是自找麻烦了。他人读起来也会想骂人。

其次,这个技法其实是反面向对象的。如果你的类继承层次多于一层,就会产生问题。

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 SoapArgsout T where T : SoapArgsT { } public class GenericSoapArgsT : SoapArgsGenericSoapArgsT { } public class DerivedGenericSoapArgsT : GenericSoapArgsT { }

这三个类(或接口)的关系很一目了然对吧。又有这样一个函数,负责把SoapArgs发出去。

public class SoapSender { public virtual void SendSoapArgsT(T args) where T : SoapArgsT { } }

也很简单对吧?逻辑上,这个函数可以接受前面两个类的实例对吧?可实际上,下面第二行代码会出编译毛病。

new SoapSender().SendSoapArgs(new GenericSoapArgsint()); new SoapSender().SendSoapArgs(new DerivedGenericSoapArgsint());错误信息是:

The type 'DerivedGenericSoapArgsint' cannot be used as type parameter 'T' in the generic type or method 'ndSoapArgsT(T)'. There is no implicit reference conversion from 'DerivedGenericSoapArgsint' to 'SoapArgs DerivedGenericSoapArgsint'.

解决办法倒也算简单,让DerivedGenericSoapArgs自己再实现1遍SoapArgs接口就可以了虽然它的父类已实现了。

餐后血糖正常值
治疗拉肚子方法
鼻窦炎吃什么药管用
得了宫颈炎怎么治
孩子胃胀不消化吃什么
分享到: