我第一次看到一位同事在实施对象池时这样做了。他将要作为参数汇集的类传递给泛型基类。这个基类列出了汇集代码。
奇怪的是基类会知道它的孩子。在每种正常情况下,这被认为是不好的做法。但在这种情况下,父母只是避免编写重复代码的技术解决方案。任何其他代码都不会引用基类。
这种结构的一个缺点是它“烧毁基类”。您不能在层次结构的中间引入通用基类。此问题可能超出了主题。
以下是一个可以想象的例子:
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>
{
}
答案 0 :(得分:14)
没有;这是一个众所周知的模式,称为CRTP 它在C ++中作为虚拟方法的替代方法特别有用。
您可以在IComparable<T>
和IEquatable<T>
中的.Net框架内看到它。
为了增强稳健性,您应该添加where T : Singleton<T>
答案 1 :(得分:3)
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.
}
}
在阅读CRTP时,值得记住的是,C ++模板只是表面上与C#泛型类似。关键的区别在于模板实际上是一个在编译时工作的代码生成工具,而C#泛型在运行时是受支持的。
此外,编写这样的代码会降低可读性。因此,尽管确实存在这种方法,但您应该考虑一下您正在尝试解决的问题,看看是否有更简单的方法。