如何在使用通用约束时使用继承

时间:2010-09-02 12:12:26

标签: c# generics inheritance

在尝试实现允许继承并希望有人可以提供帮助的库时,我正在努力解决一些通用约束问题。

我正在尝试构建一个具有3种风格的类库,每个类都建立在另一个之上。对我来说,这似乎是一个使用泛型的绝佳机会,因为我不能通过纯粹的继承做我想做的事。下面的代码(这应该直接粘贴到VS)后面有一些解释:

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
}

代码背后的想法是有一组基类使用泛型公开属性。这允许我有强类型集合,例如。

在这三个阶段中有2个基于这些,TSP和VRP在示例中有4个具体类(这些是使用类库的开发人员应该与之交互,因为通用约束只是有点疯狂) - 元素,访问,路线和解决方案。

TSP和VRP也有一些前缀为Generic的类。这些允许我想要的继承,因为它暴露了通用类型。如果我不使用这些(比如Concrete_VRPRoute继承Concrete_TSPRoute),那么我必须继续强制转换Visits集合返回的项目类型以获取Capacity属性。

我相信所有类型都能正确排列,但是当我尝试构建时,我会遇到以下错误,而且我真的不知道如何处理它们。

  

错误1类型“V”不能用作   在泛型类型中输入参数“V”   或方法'Test.Generic_Route'。   没有隐含的参考   从'V'转换为   'Test.Generic_Visit'。

     

错误2   类型'V'不能用作类型   参数'V'在泛型类型或   方法'Test.Generic_Solution'。   没有隐含的参考   从'V'转换为   'Test.Generic_Visit'。

     

错误3   类型'R'不能用作类型   参数'R'在泛型类型中或   方法'Test.Generic_Solution'。   没有隐含的参考   从'R'转换为   'Test.Generic_Route'

     

错误4类型“V”不能用作   在泛型类型中输入参数“V”   或方法   'Test.Generic_TSPRoute'。有   没有隐式引用转换   'V'到'Test.Concrete_TSPVisit'。

     

错误5类型“V”不能用作   在泛型类型中输入参数“V”   或方法   'Test.Generic_TSPSolution'。   没有隐含的参考   从'V'转换为   'Test.Concrete_TSPVisit'。

     

错误6   类型'R'不能用作类型   参数'R'在泛型类型中或   方法   'Test.Generic_TSPSolution'。   没有隐含的参考   从'R'转换为   'Test.Concrete_TSPRoute'。

     

错误7   类型'Test.Concrete_VRPVisit'不能   用作类型参数'V'   通用类型或方法   'Test.Generic_TSPSolution'。   没有隐含的参考   转换自   'Test.Concrete_VRPVisit'来   'Test.Concrete_TSPVisit'。

     

错误8   类型'Test.Concrete_VRPRoute'不能   用作类型参数'R'   通用类型或方法   'Test.Generic_TSPSolution'。   没有隐含的参考   转换自   'Test.Concrete_VRPRoute'来   'Test.Concrete_TSPRoute'。 'Test.Concrete_TSPRoute'。

3 个答案:

答案 0 :(得分:6)

这是一块通用蛋糕。您需要根据自己定义泛型类。递归通用定义。

基础课程:

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课程:

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课程:

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> { }

最终结果是非通用的具体类,可以像这样使用:

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();

享受! 享受!

答案 1 :(得分:2)

好的,我们来看看第一个。错误是:

  

类型'V'不能用作泛型类型或方法'Test.Generic_Route'中的类型参数'V'。没有从'V'到'Test.Generic_Visit'的隐式引用转换。

它抱怨这个声明:

public abstract class Generic_TSPRoute<V, E> : Generic_Route<V, E>
    where V : Concrete_TSPVisit
    where E : Concrete_TSPNode

这确定了两个定义:

  • VConcrete_TSPVisit(或其后代)

  • EConcrete_TSPNode(或其后代)

现在让我们看看Generic_Route<V, E>让我们放入的内容:

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

第二个约束很好,因为Concrete_TSPNodeGenericElement。第一个是有问题的:请记住EConcrete_TSPNode 或其后代,因此Generic_Visit<E>可以是:

  • Generic_Visit<Concrete_TSPNode>

  • Generic_Visit<some subclass of Concrete_TSPNode>

但是,我们之前也知道VConcrete_TSPVisit(或其后代)。

  • Concrete_TSPVisit继承自Generic_TSPVisit<Concrete_TSPNode>

  • Generic_TSPVisit<Concrete_TSPNode>继承自Generic_Visit<Concrete_TSPNode>

注意什么?这要求它是Generic_Visit<Concrete_TSPNode>。强调不允许Generic_Visit<some subclass of Concrete_TSPNode>

换句话说,想象一下我写这个:

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

根据您的层次结构,Concrete_TSPVisitGeneric_Visit<Concrete_TSPNode>,因此具有类似

的属性
public Concrete_TSPNode Element { get; set; }

如果我从此属性中检索值,则只能保证Concrete_TSPNode,但不一定是Concrete_TSPNode_Subclass

编辑:

我会留下这个答案,因为它解释了编译器错误的原因,但是Enigmativity的答案实际上提供了问题的解决方案。

答案 2 :(得分:0)

这与Enigmativity的答案相同,但删除了所有冗余约束。这仍然编译,它没有任何通用的递归,并且据我所知,仍然是所需的类型安全。谜团,我错过了什么? :)

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> { }