如何在使用通用约束时使用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
(或它的后代) -
E
是Concrete_TSPNode
(或它的后代)
现在让我们看看Generic_Route
允许我们放入的内容:
public class Generic_Route where V : Generic_Visit where E : GenericElement
第二个约束很好,因为Concrete_TSPNode
是GenericElement
。 第一个是有问题的:记住E
是Concrete_TSPNode
或它的后代 ,因此Generic_Visit
可以是:
-
Generic_Visit
, 或 -
Generic_Visit
但是,我们之前也知道V
是Concrete_TSPVisit
(或者它的后代)。
-
Concrete_TSPVisit
inheritance自Generic_TSPVisit
-
Generic_TSPVisit
inheritance自Generic_Visit
注意什么? 这要求它是Generic_Visit
。 重点不允许是Generic_Visit
。
换句话说,想象一下我写这个:
var route = new Generic_TSPRoute();
根据您的层次结构, Concrete_TSPVisit
是Generic_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 { }