views:

153

answers:

3

Let's say I have an interface like that:

interface IAwesome
{
    T DoSomething<T>();
}

Is there any way to implement the DoSomething method with type constraint? Obviously, this won't work:

class IncrediblyAwesome<T> : IAwesome where T : PonyFactoryFactoryFacade
{
    public T DoSomething()
    {
        throw new NotImplementedException();
    }
}

This obviously won't work because this DoSomething() won't fully satisfy the contract of IAwesome - it only work for a subset of all possible values of the type parameter T. Is there any way to get this work short of some "casting black magic" (which is what I'm going to do as last resort if the answer is no)?

Honestly, I don't think it's possible but I wonder what you guys think.

EDIT: The interface in question is System.Linq.IQueryProvider so I can't modify the interface itself.

+2  A: 

Wouldn't something like this do the trick?

interface IAwesome<U>
{
    T DoSomething<T>() where T : U
}

class IncrediblyAwesome<T> : IAwesome<PonyFactoryFactoryFacade>
{
    public T DoSomething()
    {
        throw new NotImplementedException();
    }
}

I'm not sure if this compiles.

Zyphrax
Uunfortunately, the inteface in question is not defined by me - it is part of the .NET framework (System.Linq.IQueryProvider) so I can't really put type constraints on the inteface. Thanks for the suggestion though.
DrJokepu
Can you get away with IAwesome<U> inheriting IQueryProvider? Or would that break your code?
Zyphrax
That would not help either because you cannot restrict an existing method in an interface; all warranties made by the base interface must remain true for all descendants and implementors (contract-wise, of course you can make runtime checks if needed).
Lucero
@Lucero: you're right, the contract wouldn't match. Sorry doc, I'm out of solutions :)
Zyphrax
+6  A: 

No, this cannot work by design, since it would mean that the contract for IAwesome would not be (fully) satisfied.

As long as IncrediblyAwesome<T> implements IAwesome, one is allowed to do this:

IAwesome x = new IncrediblyAwesome<Something>()

Obviously, with your additional constraint, this could not work, since the user of IAwesome cannot know of the restrictions put on it.

In your case, the only solution I can think of is this (doing runtime checking):

interface IAwesome { // assuming the same interface as in your sample
    T DoSomething<T>();
}

class IncrediblyAwesome<TPony> : IAwesome where TPony : PonyFactoryFactoryFacade {
    IAwesome.DoSomething<TAnything>() {
        return (TAnything)((object)DoSomething()); // or another conversion, maybe using the Convert class
    }

    public TPony DoSomething() {
        throw new NotImplementedException();
    }
}
Lucero
Yeah, that's what I was afraid of.
DrJokepu
You've used "T" to mean three different but closely related things in this code. That's very confusing; can you make at least one of them named something else?
Eric Lippert
Yeah, sorry, copy-paste is evil... Fixed - thanks for the hint. ;)
Lucero
+1  A: 

As you've already mentioned, such a solution cannot work.

Moreover, your example violates the contract in another way: One instance of IncrediblyAwesome is always bound to one specific descendant of PonyFactoryFactoryFacade (btw, I'd be really interested in the purpose of this class :-)). Therefore, the concrete implementation of DoSomething would not be a generic method as specified in the interface, but always return one single type. You couln't write:

IAwesome a = new IncrediblyAwesome<SomePonyFacadeFactory1>();
SomePonyFacadeFactory2 facade2 = a.DoSomething<SomePonyFacadeFactory2>();

even though both facade-factories decend from PonyFactoryFactoryFacade ...

Another comment: I would stay away from black magic casting, since this problems are inherent to your design and not just a shortcoming of C# ...

MartinStettner