views:

505

answers:

1

Is there a good, generic, way to do the following without resorting to a second method or lots of casts - I want to keep the API as light as possible and it seems ok to me OO-wise:

class Foo
{
  public T Bar<T>() where T: IAlpha
  {
    /* blahblahblah */
  }

  public T Bar<T>() where T: IBeta
  {
    /* blahblahblah */
  }
}

interface IAlpha
{
  string x {set;}
}

interface IBeta
{
  string y {set;}
}

thanks

+6  A: 

You can't overload a method by return value only (generic or not). Additionally, it would be impossible to resolve calls to Bar, since an object could implement both IAlpha and IBeta, so this is not possible using overloading.

public class AlphaBeta : IAlpha, IBeta
{
    string x {set;}
    string y {set;}
}

// too ambiguous
AlphaBeta parkingLot = myFoo.Bar<AlphaBeta>();

The below will also not work, because the methods only differ by return type

class Gar
{
    public string Foo()
    {
        return "";
    }

    public int Foo()
    {
        return 0;
    }
}

Unfortunately, your best solution will be to use a less generic solution. The command pattern might serve you well here.

public class Foo
{
    private readonly static Dictionary<Type, Command> factories =
        new Dictionary<Type, Command>();

    static Foo()
    {
        factories.Add(typeof(IAlpha), new AlphaCreationCommand());
        factories.Add(typeof(IBeta), new BetaCreationCommand());
    }

    public T Bar<T>()
    {
        if (factories.ContainsKey(typeof(T)))
        {
            return (T) factories[typeof(T)].Execute();
        }
        throw new TypeNotSupportedException(typeof(T));
    }
}

// use it like this
IAlpha alphaInstance = myFoo.Bar<IAlpha>();
IBeta betaInstance = myFoo.Bar<IBeta>();

Another way of implementing Bar which allows you to call it without explicitly declaring the type (in your angled brackets) is to use an out parameter. I'd avoid it, however, since out parameters in 100% managed usually stink of bad design.

public void Bar<T>(out T returnValue)
{
    if (factories.ContainsKey(typeof(T)))
    {
        returnValue = (T) factories[typeof(T)].Execute();
        return;
    }
    throw new TypeNotSupportedException(typeof(T));
}

// call it like this
// T is inferred from the parameter type
IAlpha alphaInstance;
IBeta betaInstance;
myFoo.Bar(out alphaInstance);
myFoo.Bar(out betaInstance);

I excluded Command, AlphaCreationCommand, BetaCreationCommand, and TypeNotSupportedException. Their implementation should be fairly self explanatory.

Alternately, you can use Func instead of Commands, but this forces you to implement all of your instantiation code in Foo which can get out of hand as your code base grows.

Michael Meadows
It won't compile specifically for types that implement both. It *will* compile for all other types though (your wordage implies that it won't compile, period, because of this).
Colin Burnett
I'm not aware of when method overloading by return type only will compile. If you can provide an example, I can edit my answer to correct it.
Michael Meadows