Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/asp.net-mvc-3/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 让一个类将自身作为参数传递给泛型基类是一种罪恶吗?_C#_Oop_Generics_Design Patterns - Fatal编程技术网

C# 让一个类将自身作为参数传递给泛型基类是一种罪恶吗?

C# 让一个类将自身作为参数传递给泛型基类是一种罪恶吗?,c#,oop,generics,design-patterns,C#,Oop,Generics,Design Patterns,我第一次看到一位同事在实现对象池时这样做。他将要作为参数合并的类传递给泛型基类。这个基类展示了池代码 奇怪的是,底层阶级会知道自己的孩子。在任何正常情况下,这都被认为是不好的做法。但在这种情况下,父级只是一种避免编写重复代码的技术解决方案。基类从未被任何其他代码引用 这种构造的一个缺点是它“烧坏了基类”。不能在层次结构的中间引入泛型基类。这个问题可能超出了主题范围 下面是一个值得思考的例子: public abstract class Singleton<T> where T : c

我第一次看到一位同事在实现对象池时这样做。他将要作为参数合并的类传递给泛型基类。这个基类展示了池代码

奇怪的是,底层阶级会知道自己的孩子。在任何正常情况下,这都被认为是不好的做法。但在这种情况下,父级只是一种避免编写重复代码的技术解决方案。基类从未被任何其他代码引用

这种构造的一个缺点是它“烧坏了基类”。不能在层次结构的中间引入泛型基类。这个问题可能超出了主题范围

下面是一个值得思考的例子:

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>
{
}
公共抽象类单例,其中T:class
{
公共静态T实例{get;private set;}
公共单身人士()
{
if(实例!=null)
抛出新异常(“已创建单例实例”);
实例=(T)(对象)此;
}
}
公共类MyClass:Singleton
{
}
改进代码:

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>
{
}
公共抽象类Singleton,其中T:Singleton
{
公共静态T实例{get;private set;}
公共单身人士()
{
if(实例!=null)
抛出新异常(“已创建单例实例”);
实例=(T)这个;
}
}
公共类MyClass:Singleton
{
}

否;这是一种众所周知的模式,称为。
它在C++中作为一种替代虚拟方法特别有用。 您可以在.Net framework的
IComparable
IEquatable
中看到它


为了增加健壮性,应该添加
,其中T:Singleton

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.
    }
}
公共抽象类饮料,其中T:饮料
{
公共摘要T混合物(T-drink1,T-drink2);
}
公共茶:饮料
{
公共混合茶(茶饮料1、茶饮料2)
{ 
//在这里沏茶。
}
}
公共级咖啡:饮料
{
公共咖啡混合饮料(咖啡饮料1、咖啡饮料2)
{ 
//在这里混合咖啡。虽然咖啡很难喝,但是
//我不明白你为什么要这么做。
}
}
<> P>阅读CRTP时,值得记住的是C++模板只是表面上类似于C泛型。关键区别在于模板是一种有效的代码生成工具,在编译时工作,而C#泛型在运行时受支持


此外,编写这样的代码会降低可读性。因此,尽管在某些情况下,这肯定是正确的方法,但您应该思考一下您试图解决的问题,看看是否有更直接的方法。

最后一句话让我大吃一惊。单件在哪里T:单件?这难道不需要您进行无休止的递归(每个t都必须是一个单例,而那个t又将是一个单例,等等)添加了SLaks代码改进。第一行确实让你头晕目眩。@Kristof看上面的例子:
publicMyClass:Singleton
。类型,
MyClass
是-A
Singleton
,因此它可以工作。不过,它仍然有点大脑混乱。经过几次的脑力处理之后,它确实是增加了:“仍然部分地大脑煎炸,但是,YES,它是正确的:”也看到下面的讨论,它断言它与C++ CRTP不一样:MyClass。实例编译。当然,<代码>公共类咖啡:饮料< /代码>是一个合法声明。这很容易搞砸。是的,尽管当您尝试使用
Blend()
方法并意识到它需要
Tea
类型时,这是显而易见的。不过,你是对的——很容易把事情搞砸。它也不容易阅读。