Inheritance on a Constrained Generic Type Parameter

Inheritance on a constrained generic type parameter

Because you can't. Generics are not templates. You shouldn't think about them like C++ templates and expect the same behavior. They are fundamentally different concepts.

The C# specification explicitly prohibits usage of type parameters as base class:

C# 3.0 Language Specification: Type Parameters (§4.5)


A type parameter cannot be used directly to declare a base class (§10.2.4) or interface (§13.1.3).

Update:

I understand what you want to do and its use. This is a traditional use case of C++ templates. Specifically, if this was possible to do using C# generics, things like Moq library could benefit from it. The problem is, C++ templates are compile time "find and replace" constructs while C# generics are a run time thing.

To demonstrate this fact, for this class:

class Test<T> where T : class {
// whatever contents it might have...
}

only a single IL will be emitted at compile time and at run time, the JIT compiler would generate a single native code for all reference-type type parameters. This is not like C++ templates at all, where native code would be emitted for every T separately (it's subject to optimization but conceptually, they are completely separate pieces of code).

Generic inheritance constraint on class parameter fails

This is because you can only constrain type parameters that are defined in the method. T is not defined in the method, but in the class, so you can't constrain it like that.

You can, however, make Cast an extension method:

// in some static class
public static Test<NewT> Cast<T, NewT>(this Test<T> test) where T : NewT {
return new Test<NewT>() { something = test.something };
}

This means that something has to be made public, which can sometimes be undesirable. You can alternatively do something like this:

public class Test<T>
{
public T Something { get; private set; }

public Test(T something) {
Something = something;
}

}

// in some static class
public static Test<NewT> Cast<T, NewT>(this Test<T> test) where T : NewT
{
return new Test<NewT>(test.Something);
}

Why aren't generic type constraints inheritable/hierarchically enforced

ANOTHER UPDATE:

This question was the subject of my blog in July 2013. Thanks for the great question!

UPDATE:

I've given this some more thought and I think the problem is that you don't want inheritance at all. Rather, what you want is for all constraints that must be placed on a type parameter in order for that type parameter to be used as a type argument in another type to be automatically deduced and invisibly added to the declaration of the type parameter. Yes?

Some simplified examples:

class B<T> where T:C {}
class D<U> : B<U> {}

U is a type parameter that is used in a context where it must be C. Therefore in your opinion the compiler should deduce that and automatically put a constraint of C on U.

What about this?

class B<T, U> where T : X where U : Y {}
class D<V> : B<V, V> {}

Now V is a type parameter used in a context where it must be both X and Y. Therefore in your opinion the compiler should deduce that and automatically put a constraint of X and Y on V. Yes?

What about this?

class B<T> where T : C<T> {}
class C<U> : B<D<U>> where U : IY<C<U>> {}
class D<V> : C<B<V>> where V : IZ<V> {}

I just made that up, but I assure you that it is a perfectly legal type hierarchy. Please describe a clear and consistent rule that does not go into infinite loops for determining what all the constraints are on T, U and V. Don't forget to handle the cases where type parameters are known to be reference types and the interface constraints have covariance or contravariance annotations! Also, the algorithm must have the property that it gives exactly the same results no matter what order B, C and D appear in source code.

If inference of constraints is the feature you want then the compiler has to be able to handle cases like this and give clear error messages when it cannot.

What is so special about base types? Why not actually implement the feature all the way?

class B<T> where T : X {}
class D<V> { B<V> bv; }

V is a type parameter used in a context where it must be convertible to X; therefore the compiler should deduce this fact and put a constraint of X on V. Yes? Or no?

Why are fields special? What about this:

class B<T> { static public void M<U>(ref U u) where U : T {} }
class D<V> : B<int> { static V v; static public void Q() { M(ref v); } }

V is a type parameter used in a context where it can only be int. Therefore the C# compiler should deduce this fact and automatically put a constraint of int on V.

Yes? No?

You see where this is going? Where does it stop? In order to implement your desired feature properly the compiler must do whole-program analysis.

The compiler does not do this level of analysis because that is putting the cart before the horse. When you construct a generic, you are required to prove to the compiler that you've satisfied the constraint. It's not the compiler's job to figure out what you meant to say and work out what further set of constraints satisfy the original constraint.

For similar reasons, the compiler also does not attempt to automatically infer variance annotations in interfaces on your behalf. See my article on that subject for details.

http://blogs.msdn.com/b/ericlippert/archive/2007/10/29/covariance-and-contravariance-in-c-part-seven-why-do-we-need-a-syntax-at-all.aspx


Original answer:

I would like to know why aren't generic type constraints inheritable?

Only members are inherited. A constraint is not a member.

if my inherited class inherits from base class and passes over its generic type which has a constraint on the base class it automatically means that generic type in inherited class should have the same constraint without explicitly defining it. Shouldn't it?

You're just asserting how something should be, without providing any explanation of why it should be that way. Explain to us why you believe that the world should be that way; what are the benefits and what are the drawbacks and what are the costs?

Am I doing something wrong, understanding it wrong or is it really that generic type constraint aren't inheritable?

Generic constraints are not inherited.

If the latter is true, why in the world is that?

Features are "not implemented" by default. We don't have to provide a reason why a feature is not implemented! Every feature is not implemented until someone spends the money to implement it.

Now, I hasten to note that generic type constraints are inherited on methods. Methods are members, members are inherited, and the constraint is a part of the method (though not part of its signature). So the constraint comes along with the method when it is inherited. When you say:

class B<T> 
{
public virtual void M<U>() where U : T {}
}

class D<V> : B<IEnumerable<V>>
{
public override void M<U>() {}
}

Then D<V>.M<U> inherits the constraint and substitutes IEnumerable<V> for T; thus the constraint is that U must be convertible to IEnumerable<V>. Note that C# does not allow you to restate the constraint. This is in my opinion a misfeature; I would like to be able to restate the constraint for clarity.

But D does not inherit any kind of constraint on T from B; I don't understand how it possibly could. M is a member of B, and is inherited by D along with its constraint. But T is not a member of B in the first place, so what is there to inherit?

I'm really not understanding at all what feature it is that you want here. Can you explain with more details?

Inherit from a generic base class, apply a constraint, and implement an interface in C#

You include the entire signature of your class before you define generic constraints.

class DerivedFoo<T1, T2> : ParentFoo<T1, T2>, IFoo where T2 : IBar
{
...
}

F# Generic constraint to have one generic type inherit from another

As from the F# spec:

New constraints of the form type :> 'b are solved again as type = 'b.

There are some popular F# language suggestions that aim to solve this, see:

https://github.com/fsharp/fslang-suggestions/issues/255

https://github.com/fsharp/fslang-suggestions/issues/162

How to use Inheritance when using Generic Constraints

It's a piece of generic cake. You need to define the generic classes in terms of themselves. A recursive generic definition.

Base Classes:

public class Generic_Element<E>
where E : Generic_Element<E>
{
}

/// <summary>Visit to a Generic_Element</summary>
public class Generic_Visit<V, E>
where V : Generic_Visit<V, E>
where E : Generic_Element<E>
{
public E Element { get; set; }
}

/// <summary>Collection of Visits</summary>
public class Generic_Route<R, V, E>
where R : Generic_Route<R, V, E>
where V : Generic_Visit<V, E>
where E : Generic_Element<E>
{
public List<V> Visits { get; set; }
public Double Distance { get; set; }
}

/// <summary>Collection of Routes</summary>
public class Generic_Solution<S, R, V, E>
where S : Generic_Solution<S, R, V, E>
where R : Generic_Route<R, V, E>
where V : Generic_Visit<V, E>
where E : Generic_Element<E>
{
public List<R> Routes { get; set; }

public Double Distance
{
get
{
return this.Routes.Select(r => r.Distance).Sum();
}
}
}

TSP Classes:

public class Generic_Tsp_Element<E> : Generic_Element<E>
where E : Generic_Tsp_Element<E>
{
}

/// <summary>Visit to a Generic_Element</summary>
public class Generic_Tsp_Visit<V, E> : Generic_Visit<V, E>
where V : Generic_Tsp_Visit<V, E>
where E : Generic_Tsp_Element<E>
{
public Double Time { get; set; }
}

/// <summary>Collection of Visits</summary>
public class Generic_Tsp_Route<R, V, E> : Generic_Route<R, V, E>
where R : Generic_Tsp_Route<R, V, E>
where V : Generic_Tsp_Visit<V, E>
where E : Generic_Tsp_Element<E>
{
public Double Time
{
get
{
return this.Visits.Select(v => v.Time).Sum();
}
}
}

/// <summary>Collection of Routes</summary>
public class Generic_Tsp_Solution<S, R, V, E> : Generic_Solution<S, R, V, E>
where S : Generic_Tsp_Solution<S, R, V, E>
where R : Generic_Tsp_Route<R, V, E>
where V : Generic_Tsp_Visit<V, E>
where E : Generic_Tsp_Element<E>
{
public Double Time
{
get
{
return this.Routes.Select(r => r.Time).Sum();
}
}
}

public class Concrete_Tsp_Element : Generic_Tsp_Element<Concrete_Tsp_Element> { }

public class Concrete_Tsp_Visit : Generic_Tsp_Visit<Concrete_Tsp_Visit, Concrete_Tsp_Element> { }

public class Concrete_Tsp_Route : Generic_Tsp_Route<Concrete_Tsp_Route, Concrete_Tsp_Visit, Concrete_Tsp_Element> { }

public class Concrete_Tsp_Solution : Generic_Tsp_Solution<Concrete_Tsp_Solution, Concrete_Tsp_Route, Concrete_Tsp_Visit, Concrete_Tsp_Element> { }

VRP Classes:

public class Generic_Vrp_Element<E> : Generic_Element<E>
where E : Generic_Vrp_Element<E>
{
}

/// <summary>Visit to a Generic_Element</summary>
public class Generic_Vrp_Visit<V, E> : Generic_Visit<V, E>
where V : Generic_Vrp_Visit<V, E>
where E : Generic_Vrp_Element<E>
{
public Double Capacity { get; set; }
}

/// <summary>Collection of Visits</summary>
public class Generic_Vrp_Route<R, V, E> : Generic_Route<R, V, E>
where R : Generic_Vrp_Route<R, V, E>
where V : Generic_Vrp_Visit<V, E>
where E : Generic_Vrp_Element<E>
{
public Double Capacity
{
get
{
return this.Visits.Select(v => v.Capacity).Sum();
}
}
}

/// <summary>Collection of Routes</summary>
public class Generic_Vrp_Solution<S, R, V, E> : Generic_Solution<S, R, V, E>
where S : Generic_Vrp_Solution<S, R, V, E>
where R : Generic_Vrp_Route<R, V, E>
where V : Generic_Vrp_Visit<V, E>
where E : Generic_Vrp_Element<E>
{
public Double Capacity
{
get
{
return this.Routes.Select(r => r.Capacity).Sum();
}
}
}

public class Concrete_Vrp_Element : Generic_Vrp_Element<Concrete_Vrp_Element> { }

public class Concrete_Vrp_Visit : Generic_Vrp_Visit<Concrete_Vrp_Visit, Concrete_Vrp_Element> { }

public class Concrete_Vrp_Route : Generic_Vrp_Route<Concrete_Vrp_Route, Concrete_Vrp_Visit, Concrete_Vrp_Element> { }

public class Concrete_Vrp_Solution : Generic_Vrp_Solution<Concrete_Vrp_Solution, Concrete_Vrp_Route, Concrete_Vrp_Visit, Concrete_Vrp_Element> { }

The final result is non-generic concrete classes that can be used like this:

var e = new Concrete_Tsp_Element();
var v = new Concrete_Tsp_Visit();
v.Element = e;
v.Time = 0.5;
var r = new Concrete_Tsp_Route();
r.Visits = new List<Concrete_Tsp_Visit>(new[] { v });
r.Distance = 2.1;
var s = new Concrete_Tsp_Solution();
s.Routes = new List<Concrete_Tsp_Route>(new[] { r });
Console.WriteLine(s.Distance);
Console.WriteLine(s.Time);
Console.ReadLine();

Enjoy!
Enjoy!

C# inheritance generic classes with constraints

Your interface should be:

public interface IGenericTeacher<T> where T : IGenome<T>
{
//...
}

Generic classes, constraints and inheritance

You can do this:

class ArrayWidget<T> extends Widget<Array<T>> {
setValue() {
let v = new Array<T>();
this.value = v; // ok
}
}

let aw = new ArrayWidget<string>();
aw.value = ["a", "b", "c"];

It will save you from having to specify an additional generic parameter too.



Related Topics



Leave a reply



Submit