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 class library that has 3 flavours to it, each building on top of the other. To me it seemed like a perfect opportunity to use Generics as I can't quite do what I want through pure inheritance. The code's below (this should paste straight into VS) with some explanation afterwards:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Test
{
#region Base Classes
public class GenericElement { }
/// <summary>Visit to a GenericElement</summary>
public class Generic_Visit<E> where E : GenericElement
{
public E Element { get; set; }
}
/// <summary>Collection of Visits</summary>
public class Generic_Route<V, E>
where V : Generic_Visit<E>
where E : GenericElement
{
public List<V> Visits { get; set; }
public Double Distance { get; set; }
}
/// <summary>Collection of Routes</summary>
public class Generic_Solution<R, V, E>
where R : Generic_Route<V, E>
where V : Generic_Visit<E>
where E : GenericElement
{
public List<R> Routes { get; set; }
public Double Distance
{
get
{
return this.Routes.Select(r => r.Distance).Sum();
}
}
}
#endregion
#region TSP Classes
public class Concrete_TSPNode : GenericElement { }
public abstract class Generic_TSPVisit<E> : Generic_Visit<E>
where E : Concrete_TSPNode
{
public Double Time { get; set; }
}
public abstract class Generic_TSPRoute<V, E> : Generic_Route<V, E>
where V : Concrete_TSPVisit
where E : Concrete_TSPNode
{
public Double Time
{
get
{
return this.Visits.Select(v => v.Time).Sum();
}
}
}
public abstract class Generic_TSPSolution<R, V, E> : Generic_Solution<R, V, E>
where R : Concrete_TSPRoute
where V : Concrete_TSPVisit
where E : Concrete_TSPNode
{
public Double Time
{
get
{
return this.Routes.Select(r => r.Time).Sum();
}
}
}
public class Concrete_TSPVisit : Generic_TSPVisit<Concrete_TSPNode> { }
public class Concrete_TSPRoute : Generic_TSPRoute<Concrete_TSPVisit, Concrete_TSPNode> { }
public class Concrete_TSPSolution : Generic_TSPSolution<Concrete_TSPRoute, Concrete_TSPVisit, Concrete_TSPNode> { }
#endregion
#region VRP
public class Concrete_VRPNode : Concrete_TSPNode { }
public abstract class Generic_VRPVisit<E> : Generic_TSPVisit<E> where E : Concrete_VRPNode
{
public Double Capacity { get; set; }
}
public abstract class Generic_VRPRoute<V, E> : Generic_TSPRoute<V, E>
where V : Concrete_VRPVisit
where E : Concrete_VRPNode
{
public Double Capacity
{
get
{
return this.Visits.Select(v => v.Capacity).Sum();
}
}
}
public abstract class G_VRPSolution<R, V, E> : Generic_TSPSolution<R, V, E>
where R : Concrete_VRPRoute
where V : Concrete_VRPVisit
where E : Concrete_VRPNode
{
public Double Capacity
{
get
{
return this.Routes.Select(r => r.Capacity).Sum();
}
}
}
public class Concrete_VRPVisit : Generic_VRPVisit<Concrete_VRPNode> { }
public class Concrete_VRPRoute : Generic_VRPRoute<Concrete_VRPVisit, Concrete_VRPNode> { }
public class Concrete_VRPSolution : Generic_TSPSolution<Concrete_VRPRoute, Concrete_VRPVisit, Concrete_VRPNode> { }
#endregion
}
The idea behind the code is that there are a set of base classes that expose properties using Generics. This allows me to have strongly typed collections for example.
There are then 2 of the 3 stages build upon these, TSP and VRP in the example which have 4 concrete classes (these are what the developer using the class library should be interacting with as the generic constraints just get a bit crazy) - Element, Visit, Route and Solution.
There are also some classes prefixed Generic for both TSP and VRP. These allow the inheritance that I want as it exposes the Generic Types. If I don't use these (say Concrete_VRPRoute inherits Concrete_TSPRoute) then I have to keep casting the type of item returned by the Visits collection to get the Capacity property for example.
I'm fairly confident all the types line up correctly, but when I try to build I get the following errors and I really don't know how to tackle them.
Error 1 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'.
Error 2 The type 'V' cannot be used as type parameter 'V' in the generic type or method 'Test.Generic_Solution'. There is no implicit reference conversion from 'V' to 'Test.Generic_Visit'.
Error 3 The type 'R' cannot be used as type parameter 'R' in the generic type or method 'Test.Generic_Solution'. There is no implicit reference conversion from 'R' to 'Test.Generic_Route'
Error 4 The type 'V' cannot be used as type parameter 'V' in the generic type or method 'Test.Generic_TSPRoute'. There is no implicit reference conversion from 'V' to 'Test.Concrete_TSPVisit'.
Error 5 The type 'V' cannot be used as type parameter 'V' in the generic type or method 'Test.Generic_TSPSolution'. There is no implicit reference conversion from 'V' to 'Test.Concrete_TSPVisit'.
Error 6 The type 'R' cannot be used as type parameter 'R' in the generic type or method 'Test.Generic_TSPSolution'. There is no implicit reference conversion from 'R' to 'Test.Concrete_TSPRoute'.
Error 7 The type 'Test.Concrete_VRPVisit' cannot be used as type parameter 'V' in the generic type or method 'Test.Generic_TSPSolution'. There is no implicit reference conversion from 'Test.Concrete_VRPVisit' to 'Test.Concrete_TSPVisit'.
Error 8 The type 'Test.Concrete_VRPRoute' cannot be used as type parameter 'R' in the generic type or method 'Test.Generic_TSPSolution'. There is no implicit reference conversion from 'Test.Concrete_VRPRoute' to 'Test.Concrete_TSPRoute'.'Test.Concrete_TSPRoute'.