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

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

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

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>()

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

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

澄清.这不是仅添加T:MyType的情况,因为如果我创建一个从MyType继承的名为MySubType的类型,我将能够在MySubType的实例上调用DoStuff并将MyType作为参数传递.这也意味着在采取Action< T>的情况下.如果没有先进行强制转换,我将无法调用MySubType的方法.

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

你的“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中仔细设计新的方差特征,以确保它们始终是类型安全的. (除非数组上的现有变体转换不是类型安全的并且将继续不是类型安全的.)我们希望确保编译器可以在编译时检查所有违规约束,而不是必须吐出执行运行时的新代码检查可能意外失败.

转载注明原文:c# – 是否可以将泛型参数约束为当前对象的子类型? - 代码日志