在尝试实现允许继承并希望有人可以提供帮助的库时,我正在努力解决一些通用约束问题。
我正在尝试构建一个具有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'。
答案 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
这确定了两个定义:
V
是Concrete_TSPVisit
(或其后代)
E
是Concrete_TSPNode
(或其后代)
现在让我们看看Generic_Route<V, E>
让我们放入的内容:
public class Generic_Route<V, E>
where V : Generic_Visit<E>
where E : GenericElement
第二个约束很好,因为Concrete_TSPNode
是GenericElement
。第一个是有问题的:请记住E
是Concrete_TSPNode
或其后代,因此Generic_Visit<E>
可以是:
Generic_Visit<Concrete_TSPNode>
,或
Generic_Visit<some subclass of Concrete_TSPNode>
但是,我们之前也知道V
是Concrete_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_TSPVisit
是Generic_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> { }