views:

174

answers:

4

Excuse me if this is a dupe, but I couldn't seem to get the right combo of keywords to filter down the various type constraint and generics questions out there (as there are a lot).

I have two interfaces--let's call them IOnline and IOffline.

They're closely related in that they describe nearly identical contracts, but one of the key differences between them is the context in which the concrete implementations will be used. It's not exactly my circumstances, but it illustrates the problem well.

I then have some methods out there that do work against the concrete implementers of these interfaces. Sometimes these methods only want to deal with one type and not the other.

Simple enough:

public void DoStuff<T>(string foo) where T : IOnline {}

The kicker is implementing the code for methods that can operate on EITHER type. I thought this would be correct, but in reading the compilation error, my expectation that the constraint would be interpreted as "allow any type T to be used generically here if they implement IOnline OR IOffline", is actually being interpreted as "Allow any type T to be used generically here if they implement BOTH".

public void DoStuff<T>(string foo) where T : IOnline, IOffline {}

Trying to implement two separate methods with the same name but different constraints fails as there's obvious ambiguity issues--we're not overloading since the parameter list is the same (since the desired behavior is identical).

I could use two different names for two different methods, each with the appropriate constraint, but that seems kludgy and makes other things downstream to be a pain in the ass...doable, but not ideal.

I feel like there must be something I'm missing here... I feel perfectly comfortable in generic land but this is the first time I've ever had to accomplish what I'm after and I feel like I'm just spinning my wheels atm.

+5  A: 

Supplying multiple constraints as in your second example is indeed additive. The MSDN page on generic constraints has a bit about this.

Can you make your two interfaces inherit from a base interface, and constrain the methods to the base type?

womp
To be perfectly honest, I only recently learned that C# supported interface inheritance. In ten years of dev work, the need had just never popped up. Will have to give this a whirl and see if it ultimately gets me where I need to be.
bakasan
A: 

I think the standard way in .NET to do this is to have one interface that contains both your IOnline and IOffline functions, and then some properties that say which functions are actually implemented in a specific class. You see this pattern in various places in .NET with things like a Seek() method that might or might not be implemented and a CanSeek property that you can test.

It's perhaps not the cleanest OO design, but it works.

Mark Byers
A: 

Loses some of the compile-time checking, but I can't see any way around it... You do have to choose which you would rather use, (I'm assuming your preference would be online):

public void DoStuff<T>(string foo)
{
    Type type = typeof(T);
    if(type.GetInterfaces().Contains(typeof(IOnline)))
         doStuffOnline<T>(foo);
    else if(type.GetInterfaces().Contains(typeof(IOffline)))
         doStuffOffline<T>(foo);
    else
         throw new Exception("T must implement either IOnline or IOffline");
}

private void doStuffOnline<T>(string foo){ // can assume T : IOnline }
private void doStuffOffline<T>(string foo){ // can assume T : IOffline }
LorenVS
Have to say I agree with other people though... Better OOP ways to do this, such as a single base interface...
LorenVS
+2  A: 

This is perhaps not an answer to your question, but I spontaneously get the feeling that you may want to refactor your interfaces. From your question:

They're closely related in that they describe nearly identical contracts, but one of the key differences between them is the context in which the concrete implementations will be used.

My view of interfaces is that they are contracts. They define how something should look, not exactly how it should behave; that is the task of the implementation. Now, I have no information about your application or problem domain, but I would probably try to spend some time on identifying identical parts of those interfaces and move them into a single interface, and only keep the differencies as separate interfaces. That way you could perhaps navigate past these kinds of issues more easily.

Fredrik Mörk
It boils down to intent I suppose-- I've seen interfaces used as a contract as you describe but I've just as often seen the argument that an interface, while a contract, ultimately describes how the object behaves (as a side effect of being bound by said contract).Your point though that we might need to refactor how we distinguish between our two contexts is likely sound though. There's more nuanced reasons why we wanted interfaces though... doh!
bakasan