是否可以将泛型参数约束为当前对象的子类型?

时间:2009-11-17 16:58:34

标签: c# generics extension-methods

这是我刚刚遇到的一个有趣的问题。可以使用扩展方法做我想做的事情,但似乎不可能对类本身的成员做。

使用扩展方法,可以编写一个具有如下签名的方法:

public static void DoStuff<T>(this T arg1, T arg2)

这强制了两个参数都是你关心它的类型。与代表一起使用时,这会变得更有用。

public static void DoStuff<T>(this T arg1, Action<T> arg2)

但是我无法与会员合作。没有这样的限制:

public void DoStuff<T>(T arg1) where T : typeof(this)

如果这确实有效,那么你可以在你的基类上定义一个方法(我使用了流,因为它们是.NET中内置的层次结构):

class Stream
{
    public void DoStuff<T>(T stream) where T : this
    {
    }
}

然后在子类上就不可能像这样调用它:

ByteStream bs = new ByteStream()
bs.DoStuff(new Stream()) // Error! DoStuff() should be inferred as DoStuff<ByteStream>()

有没有办法做到这一点?我相信自动从参数中推断出类型,扩展方法是语法糖。这可能就是它起作用的原因;因为扩展方法被静态调用替换,然后允许推断类型。

我问,因为我试图将扩展方法移动到公共基类中,并且在不添加类型信息的情况下无法进行编译。

澄清。这不是仅添加where T : MyType的情况,因为如果我创建一个名为MySubType的类型继承自MyType,我将能够在DoStuff的实例上调用MySubType {1}}并传递MyType作为参数。这也意味着如果它需要Action<T>,我将无法在不首先投射的情况下调用MySubType的方法。

5 个答案:

答案 0 :(得分:9)

规则允许您使用扩展方法但不使用常规实例方法来执行此操作有多么有趣。

你的“typeof(this)”约束确实应该是“this.GetType()”。 “typeof(this)”没有任何意义; typeof采用一种类型,而不是任意表达式。

一旦你意识到,那么我们不能做这种约束的原因就变得更加明确了。 编译器始终检查约束,但显然直到运行时才能确定“this.GetType()”。这意味着如果我们有这个功能,那么我们会在运行时在类型系统中引入一个故障点:

abstract class Animal
{
    public void Mate<T>(T t) where T : this { ... CENSORED ... }
}
...
Animal x1 = new Giraffe(); 
Mammal x2 = new Tiger();
x1.Mate<Mammal>(x2); 

你不能与长颈鹿交配老虎,但程序中的哪个位置可以让编译器检测到它?无处。直到运行时才知道x1和x2的运行时类型,因此在此之前无法检测到约束违规。

我们讨厌这个。即使在编译器经过彻底检查之后,如果某个程序在任何地方都没有强制类型系统违规而失败,那么它真的很糟糕。数组协方差就是这种情况,因为我们支持数组协方差,我们不仅有时会通过编译器传递一个损坏的程序然后崩溃,我们必须减慢每次写入每个引用类型数组的速度,以便仔细检查我们没有违反类型系统。这很糟糕,我们不想在类型系统中添加更多的运行时故障点。

这就是为什么我们在C#4中仔细设计新的方差特性,以确保它们始终是类型安全的。 (除非数组上的现有变体转换不是类型安全的并且将继续不是类型安全的。)我们希望确保编译器可以在编译时检查所有违规约束,而不是必须吐出执行运行时的新代码检查可能意外失败。

答案 1 :(得分:3)

我认为你可以通过在最后指定类型来做到这一点。

public void DoStuff<T>(T arg1) where T: YourType

我目前正在解决这个问题,但是YourType是一个界面。我认为你可以用一个具体的课程来做。

答案 2 :(得分:1)

只需将基类名称用作constraint

public void DoStuff<T>( T arg1 ) where T : BaseClass

答案 3 :(得分:1)

在您澄清之后,我认为实现此目的的唯一方法是使您的基类具有通用性。它有点笨拙,但应该做你需要的。

MySubType foo = new MySubType();
MySubType bar = new MySubType();
foo.DoStuff(bar);

// ...

public class MySubType : MyBaseType<MySubType>
{
}

public class MyBaseType<T>
{
    public void DoStuff(T arg1)
    {
        // do stuff
    }
}

答案 4 :(得分:0)

将其更改为:

public void DoStuff<T>(T arg1) where T : BaseType

这样你只能使用从BaseType继承的类型T