Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 675
Jack Ryan Avatar asked Nov 17 '09 16:11

Jack Ryan


People also ask

Which of the following generic constraints restricts the generic type parameter to an object of the class?

Value type constraint If we declare the generic class using the following code then we will get a compile-time error if we try to substitute a reference type for the type parameter.

Can a generic class have multiple constraints?

Multiple interface constraints can be specified. The constraining interface can also be generic.

What is generic type constraint?

A type constraint on a generic type parameter indicates a requirement that a type must fulfill in order to be accepted as a type argument for that type parameter. (For example, it might have to be a given class type or a subtype of that class type, or it might have to implement a given interface.)

What does the generic constraint of type interface do?

Interface Type Constraint You can constrain the generic type by interface, thereby allowing only classes that implement that interface or classes that inherit from classes that implement the interface as the type parameter.


2 Answers

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.

like image 84
Eric Lippert Avatar answered Sep 27 '22 21:09

Eric Lippert


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.

like image 30
Brett Bim Avatar answered Sep 27 '22 22:09

Brett Bim