我第一次看到一个同事这样做是在他实现对象池的时候。他将将要池化的类作为参数传递给一个泛型基类。这个基类布置了池化代码。
奇怪的是基类会知道它的子类。这在任何正常情况下都被认为是不好的做法。但在这种情况下,父对象只是一种避免编写重复代码的技术解决方案。基类永远不会被任何其他代码引用。
这种结构的一个缺点是它“烧掉了基类”。不能在层次结构的中间引入泛型基类。这个问题可能超出了主题范围。
下面是一个可以想象的例子:
public abstract class Singleton<T> where T : class
{
public static T Instance { get; private set; }
public Singleton()
{
if (Instance != null)
throw new Exception("Singleton instance already created.");
Instance = (T) (object) this;
}
}
public class MyClass : Singleton<MyClass>
{
}改进的代码:
public abstract class Singleton<T> where T : Singleton<T>
{
public static T Instance { get; private set; }
public Singleton()
{
if (Instance != null)
throw new Exception("Singleton instance already created.");
Instance = (T) this;
}
}
public class MyClass : Singleton<MyClass>
{
}发布于 2013-10-08 19:51:26
不是;这是一种众所周知的模式,称为CRTP。
作为虚拟方法的替代,它在C++中特别有用。
您可以在IComparable<T>和IEquatable<T>中的.Net框架中看到它。
为了增加健壮性,您应该添加where T : Singleton<T>
发布于 2013-10-08 20:31:39
SLaks是正确的--这是一个有用的模式,通常适用于当你想在你的基类中提供强类型到你的派生类的代码时。
通常还会向泛型参数添加类型约束,以指示泛型类型必须继承自抽象类型。添加此约束的语法看起来是递归的,但不必担心-它不会递归计算,只是确保唯一有效的泛型类型是派生类。
例如,假设您经营一家茶和咖啡混合业务。对你来说,混合咖啡和咖啡、茶和茶是有意义的,但你要确保不能把咖啡和茶混合在一起。但是,因为它们都是饮料,所以您希望以相同的方式对它们进行建模。
public abstract class Beverage<T> where T : Beverage<T>
{
public abstract T Blend(T drink1, T drink2);
}
public class Tea : Beverage<Tea>
{
public override Tea Blend(Tea drink1, Tea drink2)
{
// Blend tea here.
}
}
public class Coffee : Beverage<Coffee>
{
public override Coffee Blend(Coffee drink1, Coffee drink2)
{
// Blend coffee here. Although coffee is nasty, so
// why you'd want to is beyond me.
}
}在阅读有关C#的文章时,值得记住的是C++模板只是表面上类似于CRTP泛型。关键的区别在于模板是有效的代码生成工具,它在编译时工作,而C#泛型在运行时得到支持。
此外,像这样编写代码会降低可读性。因此,尽管在某些情况下这是正确的方法,但您应该考虑一下您正在尝试解决的问题,看看是否有更直接的方法。
https://stackoverflow.com/questions/19246937
复制相似问题