开发者

C# Type Union with Interfaces

So I really like data structures and I've been working on a class library that implements different types of graphs in different ways. One of the stumbling blocks I've had is trying to easily combine specific features of different types of graphs.

To clarify, let's say I have an interface named IGraph<T>, where T is the data that each node holds. Now, I also want to have interfaces for IUndirectedGraph<T>, IDigraph<T>, and IWeightedGraph<T,E>, where E is the type used as a weight.

I'd like to be able to provide different implementations of the same types of graph. For example, I'd like to be able to provide a class that uses an adjacency list and a class that uses an adjacency matrix. The classes may have slightly different implementations of certain algorithms. As a simple example, determining the neighbors of a given object would be different in each implementation.

So, let's say I have these two class declarations:

class WeightedAdjacencyListGraph<T,E> : IUndirectedGraph<T>, IWeightedGraph<T,E>

class WeightedAdjacencyMatrixGraph<T,E> : IUndirectedGraph<T>, IWeightedGraph<T,E>

I'd like to be able to declare a variable type that can store objects of both of these classes, yet keep the functionality defined in all interfaces. Basically, I want to be able to declare a variable type like:

<IUndirectedGraph<object>+IWeightedGraph<object,double>> MyGraph = new WeightedAdjacencyListGraph<object,double>();
MyGraph = new WeightedAdjacencyMatrixGraph<object,double>();

Obviously, the variable开发者_如何学Go type declaration is not correct C# syntax, but what would I put here? Do I have to create a new interface for each combination of interfaces? Is my design fundamentally flawed, and if so, what should I do to rectify it?

Edit: I've decided to create different namespaces for directed/undirected graphs, and to store common interfaces (such as IWeightedGraph<T,E>) in the root namespace. I'll then basically create the combined interfaces I mentioned above (which were also noted in the answers). I figure the directed/undirected graphs aren't likely to share a whole lot in common when it comes to the fun algorithms anyways.


If you want to stipulate that both contracts are fulfilled in order to use a type in a certain situation, then declare a new interface that requires both, and implement that:

public interface IUndirectedAndWeightedGraph<T,E> :
    IUndirectedGraph<T>, IWeightedGraph<T,E>
{
}

Any class which implements this also fulfills the individual contracts as well, so you can still treat any class that implements IUndirectedAndWeighted as merely an IUndirected, etc.

Your theoretical approach is fundamentally flawed in the context of c#, single-inheritance polymorphism. That model requires you to define a variable as a certain, single "shape" and only objects which explicitly (not implicitly) fit that shape can be placed in that variable. It is possible to allow certain kinds of combinations like this using dynamic, but that has its own trade-offs - namely, you lose the benefits of strong-typing and interfaces.


You can do this in the limited case of method parameters by using generics and type constraints:

void ProcessGraph<TGraph>(TGraph graph)
    where TGraph: IUndirectedGraph<T>, IWeightedGraph<T,E>
{
}

The catch is that it doesn't play nice with method overloading.

More generally, though, there's no similar thing for return types or variables. As other answers note, you'll have to define a "union interface" explicitly, and make sure all classes that can implement it do so. If all interfaces in question are yours, and if you don't have many of them, this is a feasible (if tedious) approach.


I think you can just create an interface that is a combination of the interfaces that you want to have. So in your example:

IComboGraph<T, E> : IUndirectedGraph<T>, IWeightedGraph<T,E>

class WeightedAdjacencyListGraph<T,E> : IComboGraph<T, E>

class WeightedAdjacencyMatrixGraph<T,E> : IComboGraph<T, E>

Then use it as so:

IComboGraph<object, double> MyGraph = new WeightedAdjacencyListGraph<object,double>();
MyGraph = new WeightedAdjacencyMatrixGraph<object,double>();

EDIT: I should add that your combo interface doesn't have to have anything in it but be defined to inherit from those interfaces.


Your design seems reasonable to me but unfortunately there is no smart way to achieve your needs.

  1. Creating interfaces for all combinations is a possible solution but also a nightmare if you have three, four, or more interfaces.

  2. In C# 4.0 you could use dynamic. This costs a bit of strong static typing and probably some performance - I would try to avoid this, too.

  3. Where possible you could just use concrete types or var to make changes easier at the cost of tighter coupling.

  4. You could also write a wrapper that implements all interfaces and dispatch the calls to a wrapped instance yourself - also nasty.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜