开发者

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 arg开发者_开发问答1, 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):

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


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)" doesn't make any sense; typeof takes a type, not an arbitrary expression.

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

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. (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. I think you can do it with a concrete class.


Just use the base class name as the constraint.

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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜