受限泛型类型参数的继承

29

我知道无法从泛型类型参数继承,但在实现抽象类型的派生通用代理时这将非常方便 :-)

有人知道为什么不可能吗?

示例代码(C#):

abstract class Foo
{
  public virtual void Bar()
  {
     // nop
  }
}

class FooProxy<TFoo> : TFoo
  where TFoo : Foo
{

  public override void Bar()
  {
    // do some stuff before
    base.Bar();
    // do some stuff after
  }

}

编辑:这里提供更多代码,以说明如何使用它。考虑以下Foo的导数示例:

class FooX : Foo
{
  public string X { get; set; }
  public override void Bar()
  {
    Console.WriteLine("Doing Bar X");
  }
}

class FooY : Foo
{
  public string Y { get; set; }
  public override void Bar()
  {
    Console.WriteLine("Doing Bar Y");
  }
}

并且调用代码:

FooProxy<FooX> fooXProxy = new FooProxy<FooX>();
fooXProxy.X = "test X";
fooXProxy.Bar();

FooProxy<FooY> fooYProxy = new FooProxy<FooY>();
fooYProxy.Y = "test Y";
fooYProxy.Bar();

在FooProxy的Bar()方法中的代码将在使用FooX和FooY时被复用。

编辑:根据Pete OHanlon的答案进行修改:将Bar()方法改为虚方法。

3个回答

47
因为你做不到。泛型不是模板。你不应该像C++模板一样思考它们并期望相同的行为。它们是根本不同的概念。
C#规范明确禁止将类型参数用作基类:
“C# 3.0语言规范:类型参数(§4.5)” 类型参数不能直接用于声明基类(§10.2.4)或接口(§13.1.3)。
更新: 我理解你想要做的事情及其用途。这是C++模板的传统用例。具体来说,如果可以使用C#泛型来实现此操作,则可以使Moq等东西受益。问题是,C++模板是编译时的“查找和替换”结构,而C#泛型是运行时的东西。
为了证明这一事实,对于这个类:
class Test<T> where T : class {
    // whatever contents it might have...
} 

编译时和运行时只会产生一个IL,JIT编译器会为所有引用类型类型参数生成一个单独的本地代码。这与C++模板完全不同,因为对于每个T会分别发出本地代码(虽然它们可以进行优化,但在概念上,它们是完全独立的代码片段)。

2
我想要点赞,但我认为你说得对,C++模板不等同于C#泛型... - Spence
换句话说,一个对象不可能同时符合派生泛型类二进制布局(需要基类子对象具有固定、一致的大小)和基类二进制布局。 "固定大小基类" 要求是 CLR 强制实施的,并不是根本性的——可以存储到每个子对象的偏移量,在支持多重虚拟继承的语言中也需要这样做——但 .NET 选择避免了这种额外的复杂性。 - Ben Voigt

8
由于在编译时无法确定类型,因此无法从通用类型参数继承,因此编译器无法确定超类是什么。我意识到,乍一看,编译器可以弄清楚<T>是什么,似乎表明它应该能够弄清楚T是什么,但这两个东西是不同的。
此外,在Bar中存在逻辑问题。您不能调用base.Bar,因为这是一个抽象类型。为了纠正这个问题,您需要更改Foo中的实现。
public virtual void Bar() {}

1

因为Foo是一个抽象类型。

如果你在一个类中实现了Foo,然后在模板中使用它,那么你可以这样做。

我相信你也可以使用接口来实现你想要的功能。

编辑:

仔细看了一下你的代码,它没有意义。你需要创建一个基类的通用类型,然后从那个基类派生出来才能实现你想要的功能。这样做也会更有意义...

abstract class Foo<T>  
{  
    public virtual void Bar();  
}

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