简体   繁体   English

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

[英]Is it possible to constrain a generic parameter to be a subtype of the current object?

Here's an interesting problem that I have just come across. 这是我刚刚遇到的一个有趣的问题。 It is possible to do what I want using extension methods, but does not seem possible to do with members of the class itself. 可以使用扩展方法做我想做的事情,但似乎不可能对类本身的成员做。

With extension Methods it is possible to write a method that has a signature that looks like this: 使用扩展方法,可以编写一个具有如下签名的方法:

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

this enforces that both arguments are of whatever type you care calling it on. 这强制了两个参数都是你想要调用它的任何类型。 This becomes more useful when used with delegates. 与委托一起使用时,这会变得更有用。

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

However I cannot get this to work with members. 但是,我无法与成员合作。 There is no such constraint as this: 这没有这样的限制:

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

if this did work then you could define a method on your base class like this (I've used streams as they are a built in hierarchy in .NET): 如果这确实有效,那么你可以像这样在你的基类上定义一个方法(我使用了流,因为它们是.NET中内置的层次结构):

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

and then on a subclass it would not be possible to call it like this: 然后在子类上,不可能像这样调用它:

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

Is there any way of doing this? 有没有办法做到这一点? I believe that automatically inferring the types from the arguments, and extension methods are syntactic sugar. 我相信自动从参数中推断出类型,扩展方法是语法糖。 And that is probably why it works; 这可能就是它起作用的原因; because the extension methods are replaced by static calls, which then allow the type to be inferred. 因为扩展方法被静态调用替换,然后允许推断类型。

I ask because I am trying to move an extension method into a common base class, and cannot get it to compile without adding the type information. 我问,因为我试图将扩展方法移动到一个公共基类,并且无法在不添加类型信息的情况下进行编译。

To clarify. 澄清。 This isn't a case of just adding where T : MyType because if i create a type called MySubType that inherits from MyType I will be able to call DoStuff on an instance of MySubType and pass a MyType as the parameter. 这不是仅添加where T : MyType的情况,因为如果我创建一个从MyType继承的名为MySubType的类型,我将能够在MySubType的实例上调用DoStuff并将MyType作为参数传递。 This also means that in the case where it takes an Action<T> I will be unable to call methods of MySubType without casting first. 这也意味着在需要Action<T>的情况下,我将无法在不先进行MySubType情况下调用MySubType方法。

How interesting that the rules allow you to do this with extension methods but not with regular instance methods. 规则允许您使用扩展方法但不使用常规实例方法来执行此操作有多么有趣。

Your "typeof(this)" constraint really should be "this.GetType()". 你的“typeof(this)”约束确实应该是“this.GetType()”。 "typeof(this)" doesn't make any sense; “typeof(this)”没有任何意义; typeof takes a type, not an arbitrary expression. typeof采用类型,而不是任意表达式。

And once you realize that then the reason why we cannot do such a constraint becomes more clear. 一旦你意识到,那么我们不能做这种约束的原因就变得更加明确了。 Constraints are always checked by the compiler , but clearly "this.GetType()" cannot be determined until runtime. 编译器总是检查约束 ,但是直到运行时才能确定“this.GetType()”。 Which means that if we had that feature, then we'd introduce a point of failure in the type system at runtime: 这意味着如果我们有这个功能,那么我们会在运行时在类型系统中引入一个故障点:

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

You cannot mate a Tiger with a Giraffe, but where in the program can the compiler detect that? 你不能让老虎与长颈鹿交配,但程序中的哪个位置编译器可以检测到它? Nowhere. 无处。 The runtime types of x1 and x2 are not known until runtime, and so the constraint violation cannot be detected until then. 直到运行时才知道x1和x2的运行时类型,因此在此之前无法检测到约束违规。

We hate that. 我们讨厌这个。 It really sucks to have a program with no casts anywhere that nevertheless can fail with type system violations, even after having been thoroughly checked by the compiler. 即使在编译器经过彻底检查之后,如果某个程序在任何地方都没有强制类型系统违规而失败,那么它真的很糟糕。 Array covariance is just such a case, and because we support array covariance, not only do we sometimes pass a broken program through the compiler that then crashes, we have to slow down every write to every array of reference type just to double-check that we're not violating the type system. 数组协方差就是这种情况,因为我们支持数组协方差,我们不仅有时会通过编译器传递一个损坏的程序然后崩溃,我们必须减慢每次写入每个引用类型数组的速度,以便仔细检查我们没有违反类型系统。 It's awful, and we don't want to add more points of runtime failure into the type system. 这很糟糕,我们不希望在类型系统中添加更多的运行时故障点。

That's why we're carefully designing the new variance features in C# 4 to ensure that they are always typesafe. 这就是为什么我们在C#4中仔细设计新的方差特征,以确保它们始终是类型安全的。 (Except insofar as existing variant conversions on arrays are not typesafe and will continue to be not typesafe.) We want to make sure that the compiler can check all the constraints for violation at compile time, rather than having to spit new code that does runtime checks that can fail unexpectedly. (除非数组上的现有变体转换不是类型安全的并且将继续不是类型安全的。)我们希望确保编译器可以在编译时检查所有违规约束,而不是必须吐出执行运行时的新代码检查可能意外失败。

I think you may be able to do it by just specifying the type on the end. 我想你可以通过在最后指定类型来做到这一点。

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

I am doing that currently in a solution but the YourType is an interface. 我目前正在解决这个问题,但是YourType是一个界面。 I think you can do it with a concrete class. 我认为你可以用一个具体的课程来做。

只需使用基类名称作为约束

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

Following your clarification, I think the only way to achieve this is to make your base class generic. 在您澄清之后,我认为实现此目的的唯一方法是使您的基类通用。 It's a bit clumsy, but should do what you need. 这有点笨拙,但应该做你需要的。

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
    }
}

Change it to this: 把它改成这个:

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

That way you can only use type T that inherit from your BaseType 这样,您只能使用从BaseType继承的类型T.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM