How to Use Inheritance Using Common Constraints

I am struggling with some problems with a common constraint when trying to implement a library that allows inheritance and hopes someone can help.

I am trying to create a class library that has 3 options, each building on top of another. For me, this seemed like a great opportunity to use Generics, because I cannot do what I want thanks to pure inheritance. The code below (this should be inserted directly 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 is a set of base classes that expose properties using Generics. This allows, for example, to have strongly typed collections.

In this example, 2 of 3 stages are based on these, TSP and VRP in the example, which contains 4 specific classes (this is what a developer using the class library should interact with, since the general restrictions are just a little crazy) - Element, Visit, Route and Solution.

There are also some classes with a Generic prefix for TSP and VRP. They allow me to inherit what I want, since it provides generic types. If I do not use them (say Concrete_VRPRoute inherits Concrete_TSPRoute), then I need to leave the type of the element returned by the Visits collection to get the Capacity property, for example.

I am sure that all types are built correctly, but when I try to build, I get the following errors, and I really don't know how to solve them.

Mistake 1 Type ā€œVā€ cannot be used as type ā€œVā€ in the generic type or method 'Test.Generic_Route'. No implicit reference conversion from 'V' to 'Test.Generic_Visit.

Error 2 type 'V' cannot be used as type parameter "V" in the general type or method "Test.Generic_Solution". No implicit reference conversion from 'V' to 'Test.Generic_Visit.

Error 3 type "R" cannot be used as type parameter "R" in the general type or method "Test.Generic_Solution". No implicit reference conversion from 'R' to 'Test.Generic_Route'

Error 4 Type ā€œVā€ cannot be used as type ā€œVā€ is entered into the generic type or method 'Test.Generic_TSPRoute. There is no implicit link translation from 'V' - 'Test.Concrete_TSPVisit'.

Error 5 Type ā€œVā€ cannot be used as type ā€œVā€ is entered into the generic type or method 'Test.Generic_TSPSolution. No implicit reference conversion from 'V' to 'Test.Concrete_TSPVisit.

Error 6 type "R" cannot be used as type parameter "R" in the general type or method 'Test.Generic_TSPSolution. No implicit reference conversion from "R" to 'Test.Concrete_TSPRoute.

Error 7 type "Test.Concrete_VRPVisit" cannot be used as a parameter of type 'V' in the generic type or method 'Test.Generic_TSPSolution. No implicit reference conversion from 'Test.Concrete_VRPVisit' to 'Test.Concrete_TSPVisit.

Error 8 Type "Test.Concrete_VRPRoute" cannot be used as a parameter of type "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'.

+7
generics inheritance c #
source share
3 answers

This is a piece of birth cake. You must define common classes in terms of yourself. Recursive general 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 end result is not general generic classes that can be used as follows:

 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 it! Enjoy it!

+6
source share

OK, consider the first one. Mistake:

Type "V" cannot be used as a parameter of type "V" in the generic type or method "Test.Generic_Route". There is no implicit conversion of links from 'V' to 'Test.Generic_Visit'.

He complains about this expression:

 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 Concrete_TSPVisit (or its descendant)

  • E is Concrete_TSPNode (or its descendant)

Now let's see what Generic_Route<V, E> allows us to insert:

 public class Generic_Route<V, E> where V : Generic_Visit<E> where E : GenericElement 

The second restriction is excellent because Concrete_TSPNode is a GenericElement . The first one is problematic: remember that E is Concrete_TSPNode or its descendant, so Generic_Visit<E> can be:

  • Generic_Visit<Concrete_TSPNode> , or

  • Generic_Visit<some subclass of Concrete_TSPNode>

However, we also know that V is Concrete_TSPVisit (or its descendant).

  • Concrete_TSPVisit inherits from Generic_TSPVisit<Concrete_TSPNode>

  • Generic_TSPVisit<Concrete_TSPNode> inherits from Generic_Visit<Concrete_TSPNode>

Notice something? This requires that it be Generic_Visit<Concrete_TSPNode> . It is strictly not allowed to be Generic_Visit<some subclass of Concrete_TSPNode> .

In other words, imagine that I am writing this:

 var route = new Generic_TSPRoute<Concrete_TSPVisit, Concrete_TSPNode_Subclass>(); 

According to your hierarchy, Concrete_TSPVisit is Generic_Visit<Concrete_TSPNode> and therefore has a property that looks like

 public Concrete_TSPNode Element { get; set; } 

If I get a value from this property, only Concrete_TSPNode guaranteed, but not a Concrete_TSPNode_Subclass guaranteed.

EDIT:

I leave this answer because it explains the cause of the compiler error, but the Enigmativity answer does provide a solution to the problem.

+2
source share

Here is the same as Enigmativity answers, but all redundant restrictions are removed. This is still compiling, it does not have any general recursion, and as far as I can tell, it is still as safe as required. Enigmatism, 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> { } 
0
source share

All Articles