Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constrain type parameter to a base type

I know how to force a type parameter to be a subtype of another type:

public interface IMapping<T2> 
{
    public void Serialize<T3>(T3 obj) 
        where T3 : T2;
}
...

var mapping = MapManager.Find<Truck>();
mapping.Serialize(new TonkaTruck());

Is there a way to force a type parameter to be a supertype of another type?

public interface IMapping<T2>
{
    public void IncludeMappingOf<T1>() 
        where T2 : T1;   // <== doesn't work
}
...

var mapping = MapManager.Find<Truck>();

// Truck inherits Vehicle    
// Would like compiler safety here:
mapping.IncludeMappingOf<Vehicle>(); 

mapping.Serialize(new TonkaTruck());

Currently, I'm having to compare T1 and T2 at runtime using IsSubclassOf inside IncludeMappingOf. A compile-safe solution would be preferable. Any ideas?

EDIT: Changed the example to be less design-smelly.

NOTE: The linked question is quite similar, but no suitable answer is given. Hopefully this question will shed some light on that one as well.

EDIT #2:

Simpler example:

public class Holder<T2>
{
    public T2 Data { get; set; }

    public void AddDataTo<T1>(ICollection<T1> coll)
        //where T2 : T1    // <== doesn't work
    {
        coll.Add(Data);   // error
    }
}

...
var holder = new Holder<Truck> { Data = new TonkaTruck() };
var list = new List<Vehicle>();
holder.AddDataTo(list);

Compiler: Argument type 'T2' is not assignable to parameter type 'T1'. Yes I know that, I'm trying to get the compiler to allow only cases where T2 IS assignable to parameter type T1!

like image 262
CSJ Avatar asked Sep 03 '13 15:09

CSJ


People also ask

What is the purpose of the class constraint on a type parameter?

Object, you'll apply constraints to the type parameter. For example, the base class constraint tells the compiler that only objects of this type or derived from this type will be used as type arguments. Once the compiler has this guarantee, it can allow methods of that type to be called in the generic class.

What is 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.)

How do I restrict a generic class in C#?

You can specify one or more constraints on the generic type using the where clause after the generic type name. The following example demonstrates a generic class with a constraint to reference types when instantiating the generic class.

What is type constraints in C++?

A constraint is a requirement that types used as type arguments must satisfy. For example, a constraint might be that the type argument must implement a certain interface or inherit from a specific class. Constraints are optional; not specifying a constraint on a parameter is equivalent to using a Object constraint.


1 Answers

While w0lf's answer gives a direct solution, I want to give some background explanation.

When you write something like

class C<A> where A : B

or

void F<A>() where A : B

the constraints of the form A : B must have A as one of the generic type parameters on the class, interface, method, etc. being declared.

The error you are facing is not because you've placed a generic type parameter of the current declaration on the right side of the colon (that's legal) - it's because you've placed a generic type parameter of an outer declaration (not of the current declaration) on the left side of the colon.

If you want to form a constraint A : B on some declaration, A must be introduced on that declaration and the scope of A must be less than or equal to the scope of B. The reason this is a pragmatic language restriction is that, for any generic type parameter T, it isolates any reasoning about constraints on the type T to the single declaration where T is being introduced.

like image 121
Timothy Shields Avatar answered Sep 29 '22 05:09

Timothy Shields