所以我非常喜欢数据结构,我一直在研究一个以不同方式实现不同类型图形的类库。我遇到的绊脚石之一就是试图轻松组合不同类型图表的特定功能。
为了澄清,假设我有一个名为IGraph< T>的接口,其中T是每个节点保存的数据。现在,我还希望拥有IUndirectedGraph< T>,IDigraph< T>和IWeightedGraph< T,E>的接口,其中E是用作权重的类型。
我希望能够提供相同类型图表的不同实现。例如,我希望能够提供一个使用邻接列表的类和一个使用邻接矩阵的类。这些类可能具有稍微不同的某些算法的实现。举个简单的例子,确定给定对象的邻居在每个实现中都是不同的。
所以,假设我有这两个类声明:
class WeightedAdjacencyListGraph<T,E> : IUndirectedGraph<T>, IWeightedGraph<T,E>
class WeightedAdjacencyMatrixGraph<T,E> : IUndirectedGraph<T>, IWeightedGraph<T,E>
我希望能够声明一个可以存储这两个类的对象的变量类型,同时保持所有接口中定义的功能。基本上,我希望能够声明一个变量类型,如:
<IUndirectedGraph<object>+IWeightedGraph<object,double>> MyGraph = new WeightedAdjacencyListGraph<object,double>();
MyGraph = new WeightedAdjacencyMatrixGraph<object,double>();
显然,变量类型声明不是正确的C#语法,但是我会把它放在这里?我是否必须为每个接口组合创建一个新接口?我的设计是否存在根本缺陷,如果是这样,我应该怎么做才能纠正它?
编辑:我决定为有向/无向图创建不同的名称空间,并在根名称空间中存储公共接口(例如IWeightedGraph&lt; T,E&gt;)。然后,我将基本上创建上面提到的组合接口(在答案中也会注明)。我认为有针对性/无向图不太可能在有趣的算法方面共享很多共同点。
答案 0 :(得分:4)
如果你想规定两个合同都已完成以便在某种情况下使用某种类型,那么声明一个需要两者的新接口,并实现:
public interface IUndirectedAndWeightedGraph<T,E> :
IUndirectedGraph<T>, IWeightedGraph<T,E>
{
}
任何实现此功能的类也会履行单个合同,因此您仍然可以将任何实现IUndirectedAndWeighted
的类视为IUndirected
等等。
你的理论方法在c#,单继承多态的背景下存在根本缺陷。该模型要求您将变量定义为特定的单个“形状”,并且只有明确(非隐式)适合该形状的对象才能放置在该变量中。使用dynamic
可以允许某些类似的组合,但这有其自身的权衡 - 即,您失去了强类型和接口的好处。
答案 1 :(得分:2)
您可以使用泛型和类型约束在有限的方法参数中执行此操作:
void ProcessGraph<TGraph>(TGraph graph)
where TGraph: IUndirectedGraph<T>, IWeightedGraph<T,E>
{
}
问题在于doesn't play nice方法重载。
但更一般地说,返回类型或变量没有类似的东西。正如其他答案所述,您必须明确定义“联合接口”,并确保所有可以实现它的类都这样做。如果所讨论的所有接口都是你的,如果你没有很多接口,这是一种可行的(如果单调乏味的)方法。
答案 2 :(得分:1)
我认为你可以创建一个接口,它是你想拥有的接口的组合。所以在你的例子中:
IComboGraph<T, E> : IUndirectedGraph<T>, IWeightedGraph<T,E>
class WeightedAdjacencyListGraph<T,E> : IComboGraph<T, E>
class WeightedAdjacencyMatrixGraph<T,E> : IComboGraph<T, E>
然后使用它:
IComboGraph<object, double> MyGraph = new WeightedAdjacencyListGraph<object,double>();
MyGraph = new WeightedAdjacencyMatrixGraph<object,double>();
编辑:我应该补充一点,你的combo界面不必包含任何内容,但可以定义为从这些界面继承。
答案 3 :(得分:0)
您的设计对我来说似乎很合理,但遗憾的是没有明智的方法来满足您的需求。
为所有组合创建接口是一种可能的解决方案,但如果您有三个,四个或更多接口,那么这也是一场噩梦。
在C#4.0中,您可以使用dynamic
。这需要一些强大的静态类型和可能的性能 - 我也会尽量避免这种情况。
在可能的情况下,您可以使用具体类型或var
来更轻松地进行更改,但代价是更紧密的耦合。
您还可以编写一个实现所有接口的包装器,并自己将调用分派给包装实例 - 也很讨厌。