在C# 4.0中,是否可以从泛型类型参数派生一个类?

33

我一直在尝试这个,但似乎无法理清思路。我想要做的是...

public abstract class SingletonType<TSingleton, TBaseClass> : TBaseClass
    where TSingleton : TBaseClass, new()
    where TBaseClass : class
{
    static TSingleton _singleton;
    public static TSingleton Singleton
        => _singleton ?? (_singleton = new TSingleton());
}

计划是这样使用它的,这将在基类周围“包装”单例模式...

public class SingletonFoo : SingletonType<SingletonFoo, Foo> {
}

但是,我一直得到这个错误:

无法从“TBaseClass”派生,因为它是类型参数

嗯...我以为类型正是你需要从中派生的东西!

那我错过了什么吗?

注:当然,这只是一个微不足道的例子,因为它没有提供任何有用的信息。但是假设SingletonType还有很多其他与问题无关的逻辑,因此被省略以便专注于本文。


1
你说,“嗯...我以为类型就是你从中派生出来的!”——是的,但请再次阅读消息。它说的是“类型参数”,而不是“类型”。这是一个区别。 - Timwi
值得注意的是,在这里使用组合而不是继承可能更加理想。 - Mohamed Nuur
@Mohamed Nuur,请详细说明一下吗?(或者有其他人可以解释吗?)我不确定你的意思。 - Mark A. Donohoe
显示剩余2条评论
4个回答

33

C#中的泛型类型不同于C++模板,记住,泛型类型必须适用于所有可能的类型参数。而模板只需要适用于实际使用到的构造。

这个问题是一个重复问题; 查看我的答案:

为什么在C#中不能像在C++模板中一样从其中一个泛型类型参数派生?

获取更多关于此问题的思考。基本上,简短的答案是,这个功能的显著成本不足以抵消其小的好处。如果你不喜欢这个答案,请查看我的第二个答案:

为什么在C#中不能像在C++模板中一样从其中一个泛型类型参数派生?

如果你也不喜欢那个答案,请看下一个问题:

希望.NET泛型能够继承其中一个泛型参数类型的好理由是什么?


20

不,这是不可能的。例如,考虑一个声明为sealed的类型。你不能从该类继承,也没有限制只能限于非sealed类型,因此尝试通过泛型参数继承它是不可能的。


22
这并没有解释为什么编译器不能只禁止使用MyType<X>,其中X是已封闭的类型,而不是完全禁止声明MyType<T> : T。但是答案仍然是“不可能”。 - Timwi
1
好的,为什么呢?因为泛型参数必须对所有允许的类型有效;例如,new()约束允许您编写T sample = new T(),否则您的泛型参数可能是int,这不是有效的代码。如果没有nosealed约束,继承带有sealed的内容将违反C#规范。因为并非所有情况都适用于某种类型,并且不存在约束,因此无效。错误消息是编译器禁止发生这种情况的原因=D - Tejs
5
不,表面上的原因是规范中说基本类型不能是类型参数,就是这样。你试图提供的深层原因是规范为什么要这样写。你提供的是一个非解释性的回答。 - Timwi
2
顺便提一句,作为一个小挑剔,int确实有默认构造函数,因此满足new()约束条件,所有值类型也是如此。 - Timwi
1
@MarqueIV: 你的评论毫无意义。没有“object”约束条件。也许你的意思是“class”,但是你的评论是错误的:你可以有一个“new()”约束而不需要一个“class”约束。 - Timwi
显示剩余7条评论

0

每种类型都有一个确切的实际离散父类,即使涉及泛型。 即使处理开放式泛型类型(例如typeof(List<>)),您仍然可以找到父类。

如果允许您想要的内容,则不会成立,typeof(SingletonType<,>将没有父类型,这是不允许的。


-1
不,这是不可能的,因为假设你有这个类:
class bar {
  int example = 0;
}

现在这是我们的类型参数类:

class foo<T> : T {
  int example = 5;
}

如果我们创建了一个类型为foo<bar>的变量,那么example会混淆。

它们怎么会混淆呢?'Bar'是一个类型参数,而不是基类。它们完全没有关联且处于不同的作用域。 - Mark A. Donohoe
@MarqueIV 不行,因为Tfoo<bar>中的bar类型。由于foo类继承自T类(bar),所以example可以是05 - mekb
很抱歉,但那是不正确的。它们的作用域完全不同。你可以从泛型实例中访问一个示例,而你可以通过T定义的泛型内部任何实例变量来访问另一个示例。它们不是同一件事。 - Mark A. Donohoe
1
您似乎不理解 C# 中的继承如何工作。由于 bar 中的 example 是私有的,因此从 bar 派生的类可以声明自己的 example,而不会出现问题。即使 example 不是私有的,那么 foo<T> 中的 example 也会被视为“新的”。 - Emperor Eto
哦,我现在明白了,抱歉打扰了。 - mekb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接