如何在使用通用约束时使用inheritance

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

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

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Test { #region Base Classes public class GenericElement { } /// Visit to a GenericElement public class Generic_Visit where E : GenericElement { public E Element { get; set; } } /// Collection of Visits public class Generic_Route where V : Generic_Visit where E : GenericElement { public List Visits { get; set; } public Double Distance { get; set; } } /// Collection of Routes public class Generic_Solution where R : Generic_Route where V : Generic_Visit where E : GenericElement { public List 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 : Generic_Visit where E : Concrete_TSPNode { public Double Time { get; set; } } public abstract class Generic_TSPRoute : Generic_Route 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 : Generic_Solution 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 { } public class Concrete_TSPRoute : Generic_TSPRoute { } public class Concrete_TSPSolution : Generic_TSPSolution { } #endregion #region VRP public class Concrete_VRPNode : Concrete_TSPNode { } public abstract class Generic_VRPVisit : Generic_TSPVisit where E : Concrete_VRPNode { public Double Capacity { get; set; } } public abstract class Generic_VRPRoute : Generic_TSPRoute 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 : Generic_TSPSolution 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 { } public class Concrete_VRPRoute : Generic_VRPRoute { } public class Concrete_VRPSolution : Generic_TSPSolution { } #endregion } 

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

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

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

我相当自信所有类型都正确排列,但是当我尝试构建时,我得到以下错误,我真的不知道如何解决它们。

错误1类型“V”不能用作generics类型或方法“Test.Generic_Route”中的类型参数“V”。 没有从’V’到’Test.Generic_Visit’的隐式引用转换。

错误2类型“V”不能用作generics类型或方法“Test.Generic_Solution”中的类型参数“V”。 没有从’V’到’Test.Generic_Visit’的隐式引用转换。

错误3类型“R”不能用作generics类型或方法“Test.Generic_Solution”中的类型参数“R”。 没有从’R’到’Test.Generic_Route’的隐式引用转换

错误4类型“V”不能用作generics类型或方法“Test.Generic_TSPRoute”中的类型参数“V”。 没有从’V’到’Test.Concrete_TSPVisit’的隐式引用转换。

错误5类型“V”不能用作generics类型或方法“Test.Generic_TSPSolution”中的类型参数“V”。 没有从’V’到’Test.Concrete_TSPVisit’的隐式引用转换。

错误6类型“R”不能用作generics类型或方法“Test.Generic_TSPSolution”中的类型参数“R”。 没有从’R’到’Test.Concrete_TSPRoute’的隐式引用转换。

错误7类型’Test.Concrete_VRPVisit’不能在generics类型或方法’Test.Generic_TSPSolution’中用作类型参数’V’。 没有从’Test.Concrete_VRPVisit’到’Test.Concrete_TSPVisit’的隐式引用转换。

错误8类型’Test.Concrete_VRPRoute’不能用作generics类型或方法’Test.Generic_TSPSolution’中的类型参数’R’。 没有从’Test.Concrete_VRPRoute’到’Test.Concrete_TSPRoute’的隐式引用转换。’Test.Concrete_TSPRoute’。

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

基础类:

 public class Generic_Element where E : Generic_Element { } /// Visit to a Generic_Element public class Generic_Visit where V : Generic_Visit where E : Generic_Element { public E Element { get; set; } } /// Collection of Visits public class Generic_Route where R : Generic_Route where V : Generic_Visit where E : Generic_Element { public List Visits { get; set; } public Double Distance { get; set; } } /// Collection of Routes public class Generic_Solution where S : Generic_Solution where R : Generic_Route where V : Generic_Visit where E : Generic_Element { public List Routes { get; set; } public Double Distance { get { return this.Routes.Select(r => r.Distance).Sum(); } } } 

TSP课程:

 public class Generic_Tsp_Element : Generic_Element where E : Generic_Tsp_Element { } /// Visit to a Generic_Element public class Generic_Tsp_Visit : Generic_Visit where V : Generic_Tsp_Visit where E : Generic_Tsp_Element { public Double Time { get; set; } } /// Collection of Visits public class Generic_Tsp_Route : Generic_Route where R : Generic_Tsp_Route where V : Generic_Tsp_Visit where E : Generic_Tsp_Element { public Double Time { get { return this.Visits.Select(v => v.Time).Sum(); } } } /// Collection of Routes public class Generic_Tsp_Solution : Generic_Solution where S : Generic_Tsp_Solution where R : Generic_Tsp_Route where V : Generic_Tsp_Visit where E : Generic_Tsp_Element { 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 { } public class Concrete_Tsp_Route : Generic_Tsp_Route { } public class Concrete_Tsp_Solution : Generic_Tsp_Solution { } 

VRP课程:

 public class Generic_Vrp_Element : Generic_Element where E : Generic_Vrp_Element { } /// Visit to a Generic_Element public class Generic_Vrp_Visit : Generic_Visit where V : Generic_Vrp_Visit where E : Generic_Vrp_Element { public Double Capacity { get; set; } } /// Collection of Visits public class Generic_Vrp_Route : Generic_Route where R : Generic_Vrp_Route where V : Generic_Vrp_Visit where E : Generic_Vrp_Element { public Double Capacity { get { return this.Visits.Select(v => v.Capacity).Sum(); } } } /// Collection of Routes public class Generic_Vrp_Solution : Generic_Solution where S : Generic_Vrp_Solution where R : Generic_Vrp_Route where V : Generic_Vrp_Visit where E : Generic_Vrp_Element { 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 { } public class Concrete_Vrp_Route : Generic_Vrp_Route { } public class Concrete_Vrp_Solution : Generic_Vrp_Solution { } 

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

 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(new[] { v }); r.Distance = 2.1; var s = new Concrete_Tsp_Solution(); s.Routes = new List(new[] { r }); Console.WriteLine(s.Distance); Console.WriteLine(s.Time); Console.ReadLine(); 

请享用! 请享用!

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

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

它抱怨这个声明:

 public abstract class Generic_TSPRoute : Generic_Route where V : Concrete_TSPVisit where E : Concrete_TSPNode 

这确定了两个定义:

  • V是一个Concrete_TSPVisit (或它的后代)

  • EConcrete_TSPNode (或它的后代)

现在让我们看看Generic_Route允许我们放入的内容:

 public class Generic_Route where V : Generic_Visit where E : GenericElement 

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

  • Generic_Visit

  • Generic_Visit

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

  • Concrete_TSPVisitinheritance自Generic_TSPVisit

  • Generic_TSPVisitinheritance自Generic_Visit

注意什么? 这要求它是Generic_Visit 。 重点不允许是Generic_Visit

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

 var route = new Generic_TSPRoute(); 

根据您的层次结构, Concrete_TSPVisitGeneric_Visit ,因此具有看起来像的属性

 public Concrete_TSPNode Element { get; set; } 

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

编辑:

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

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

 public class Generic_Element { } public class Generic_Visit { public E Element { get; set; } } /// Collection of Visits public class Generic_Route { public List Visits { get; set; } public Double Distance { get; set; } } /// Collection of Routes public class Generic_Solution where R : Generic_Route { public List Routes { get; set; } public Double Distance { get { return this.Routes.Select(r => r.Distance).Sum(); } } } public class Generic_Tsp_Element : Generic_Element { } /// Visit to a Generic_Element public class Generic_Tsp_Visit : Generic_Visit { public Double Time { get; set; } } /// Collection of Visits public class Generic_Tsp_Route : Generic_Route where V : Generic_Tsp_Visit { public Double Time { get { return this.Visits.Select(v => v.Time).Sum(); } } } /// Collection of Routes public class Generic_Tsp_Solution : Generic_Solution where R : Generic_Tsp_Route where V : Generic_Tsp_Visit { 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 { } public class Concrete_Tsp_Route : Generic_Tsp_Route { } public class Concrete_Tsp_Solution : Generic_Tsp_Solution { } public class Generic_Vrp_Element : Generic_Element { } /// Visit to a Generic_Element public class Generic_Vrp_Visit : Generic_Visit { public Double Capacity { get; set; } } /// Collection of Visits public class Generic_Vrp_Route : Generic_Route where V : Generic_Vrp_Visit { public Double Capacity { get { return this.Visits.Select(v => v.Capacity).Sum(); } } } /// Collection of Routes public class Generic_Vrp_Solution : Generic_Solution where R : Generic_Vrp_Route where V : Generic_Vrp_Visit { 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 { } public class Concrete_Vrp_Route : Generic_Vrp_Route { } public class Concrete_Vrp_Solution : Generic_Vrp_Solution { }