How to use Inheritance when using Generic Constraints

后端 未结 3 756
梦毁少年i
梦毁少年i 2021-02-06 07:33

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

相关标签:
3条回答
  • 2021-02-06 07:53

    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.

    EDIT:

    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.

    0 讨论(0)
  • 2021-02-06 08:05

    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!

    0 讨论(0)
  • 2021-02-06 08:12

    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> { }
    
    0 讨论(0)
提交回复
热议问题