I\'m struggling with some Generic constraint issues when trying to implement a library that allows inheritance and hoping someone can help.
I\'m trying to build up a cl
OK, let’s examine the first one. The error is:
The type 'V' cannot be used as type parameter 'V' in the generic type or method 'Test.Generic_Route'. There is no implicit reference conversion from 'V' to 'Test.Generic_Visit'.
It complains about this declaration:
public abstract class Generic_TSPRoute<V, E> : Generic_Route<V, E>
where V : Concrete_TSPVisit
where E : Concrete_TSPNode
This establishes two definitions:
V
is a Concrete_TSPVisit
(or a descendent of it)
E
is a Concrete_TSPNode
(or a descendent of it)
Now let’s see what Generic_Route<V, E>
lets us put in:
public class Generic_Route<V, E>
where V : Generic_Visit<E>
where E : GenericElement
The second constraint is fine because Concrete_TSPNode
is a GenericElement
. The first one is problematic: Remember that E
is a Concrete_TSPNode
or a descendent of it, therefore Generic_Visit<E>
could be:
Generic_Visit<Concrete_TSPNode>
, or
Generic_Visit<some subclass of Concrete_TSPNode>
However, we also know from earlier that V
is a Concrete_TSPVisit
(or a descendent of it).
Concrete_TSPVisit
inherits from Generic_TSPVisit<Concrete_TSPNode>
Generic_TSPVisit<Concrete_TSPNode>
inherits from Generic_Visit<Concrete_TSPNode>
Notice something? This requires it to be a Generic_Visit<Concrete_TSPNode>
. It is emphatically not allowed to be Generic_Visit<some subclass of Concrete_TSPNode>
.
In other words, imagine I write this:
var route = new Generic_TSPRoute<Concrete_TSPVisit, Concrete_TSPNode_Subclass>();
According to your hierarchy, Concrete_TSPVisit
is a Generic_Visit<Concrete_TSPNode>
and therefore has a property that looks like
public Concrete_TSPNode Element { get; set; }
If I retrieve a value from this property, it is only guaranteed to be a Concrete_TSPNode
but not necessarily a Concrete_TSPNode_Subclass
.
I’ll leave this answer because it explains the reason for the compiler error, but Enigmativity’s answer actually provides the solution to the problem.
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!
Here is the same thing as Enigmativity’s answer, but with all the redundant constraints removed. This still compiles, it doesn’t have any generic recursion in it, and as far as I can tell is still as type-safe as was required. Enigmativity, what am I missing? :)
public class Generic_Element { }
public class Generic_Visit<E>
{
public E Element { get; set; }
}
/// <summary>Collection of Visits</summary>
public class Generic_Route<V>
{
public List<V> Visits { get; set; }
public Double Distance { get; set; }
}
/// <summary>Collection of Routes</summary>
public class Generic_Solution<R, V>
where R : Generic_Route<V>
{
public List<R> Routes { get; set; }
public Double Distance
{
get
{
return this.Routes.Select(r => r.Distance).Sum();
}
}
}
public class Generic_Tsp_Element : Generic_Element
{
}
/// <summary>Visit to a Generic_Element</summary>
public class Generic_Tsp_Visit<E> : Generic_Visit<E>
{
public Double Time { get; set; }
}
/// <summary>Collection of Visits</summary>
public class Generic_Tsp_Route<V, E> : Generic_Route<V>
where V : Generic_Tsp_Visit<E>
{
public Double Time
{
get
{
return this.Visits.Select(v => v.Time).Sum();
}
}
}
/// <summary>Collection of Routes</summary>
public class Generic_Tsp_Solution<R, V, E> : Generic_Solution<R, V>
where R : Generic_Tsp_Route<V, E>
where V : Generic_Tsp_Visit<E>
{
public Double Time
{
get
{
return this.Routes.Select(r => r.Time).Sum();
}
}
}
public class Concrete_Tsp_Element : Generic_Tsp_Element { }
public class Concrete_Tsp_Visit : Generic_Tsp_Visit<Concrete_Tsp_Element> { }
public class Concrete_Tsp_Route : Generic_Tsp_Route<Concrete_Tsp_Visit, Concrete_Tsp_Element> { }
public class Concrete_Tsp_Solution : Generic_Tsp_Solution<Concrete_Tsp_Route, Concrete_Tsp_Visit, Concrete_Tsp_Element> { }
public class Generic_Vrp_Element : Generic_Element
{
}
/// <summary>Visit to a Generic_Element</summary>
public class Generic_Vrp_Visit<V, E> : Generic_Visit<E>
{
public Double Capacity { get; set; }
}
/// <summary>Collection of Visits</summary>
public class Generic_Vrp_Route<R, V, E> : Generic_Route<V>
where V : Generic_Vrp_Visit<V, 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<R, V>
where R : Generic_Vrp_Route<R, V, E>
where V : Generic_Vrp_Visit<V, E>
{
public Double Capacity
{
get
{
return this.Routes.Select(r => r.Capacity).Sum();
}
}
}
public class Concrete_Vrp_Element : Generic_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> { }