Question

I have a base class that defines a generic method like this:

public class BaseClass
{
    public T DoSomething<T> ()
    { ... }
}

As this class is by a third-party and does not come with an interface, I am defining an interface that defines the actually needed methods from that class. That way I get loose coupling and can actually exchange that third-party class with something else. For this example, consider the following interface:

public interface ISomething
{
    T DoSomething<T> ()
        where T : Foo;
}

As you can see, it defines the same method but also applies a type constraint on the type parameter, which comes from some other requirements that are not relevant to this.

Next, I define a subtype of BaseClass which also implements ISomething. This class will be used as the usual implementation behind the interface–while the interface will be what the rest of the application will be accessing.

public class Something : BaseClass, ISomething
{
    // ...
}

As the DoSomething in BaseClass already supports any type parameter T, it should especially support a type parameter which is a subtype of Foo. So one would expect that a subtype of BaseClass already implements the interface. However I get the following error:

The constraints for type parameter 'T' of method 'BaseClass.DoSomething()' must match the constraints for type parameter 'T' of interface method 'ISomething.DoSomething()'. Consider using an explicit interface implementation instead.

Now, I have two possibilities; the first one is to do what the error suggests and implement the interface explicitely. The second is to hide the base implementation using new:

// Explicit implementation
T ISomething.DoSomething<T> ()
{
    return base.DoSomething<T>();
}

// Method hiding
public new T DoSomething<T>()
    where T : Foo
{
    return base.DoSomething<T>();
}

Both work, although I’d probably prefer the second solution to keep the method accessible from the class itself. However it still leaves the following question:

Why do I have to re-implement the method when the base type already implements it with a less-strict (read: none) type constraint? Why does the method need to be implemented exactly as it is?

edit: To give the method a bit more meaning, I changed the return type from void to T. In my actual application, I have both generic arguments and return values.

Was it helpful?

Solution 2

Certainly the given code could be compiled and run safely:

When a Something instance is typed as Something or as BaseClass the compiler would allow any type for T, while when the same instance is typed as ISomething it would allow just types inheriting Foo. In both cases you get static checking and runtime safety as usual.

In fact, the above scenario is exactly what happens when you implement ISomething explicitly. So let's see what arguments we can make for and against the current state of affairs.

For:

  • the proposed solution would not be applicable to all cases; it depends on the exact method signatures (is the type argument covariant? contravariant? invariant?)
  • it doesn't require the specification to be amended with new text stating how such cases are handled
  • it makes the code self-documenting -- you don't have to learn said text; the current rules regarding explicit interface implementation are enough
  • it does not impose development costs on the C# compiler team (documentation, feature implementation, testing, etc)

Against:

  • you need to type more

Considering the above and additionally the fact that this is not an everyday scenario, IMHO the conclusion to be reached is clear: this might be nice to have, but it certainly doesn't warrant going out of your way to implement it.

OTHER TIPS

Try using composition instead of inheritance to implement Something:

public class Something : ISomething
{
    private readonly BaseClass inner = ...;

    void DoSomething<T>() where T : Foo
    {
        inner.DoSomething<T>();
    }
}

You can get what you want with the code below. By including the type parameter in the interface defenition you can make it covariant which seems to satisfy the compiler. The Base class remains untouched and you are able to shadow the Base implementation and implement the interface with a single method.

class Program
{
    static void Main()
    {
        var something = new Something<Foo>();
        var baseClass = (BaseClass)something;
        var isomething = (ISomething<Foo>)something;

        var baseResult = baseClass.DoSomething<Bar>();
        var interfaceResult = isomething.DoSomething<Bar>();
        var result = something.DoSomething<Bar>();
    }
}

class Foo 
{
}

class Bar : Foo
{
}

class BaseClass
{
    public T DoSomething<T>()
    {
        return default(T);
    }
}

interface ISomething<out T> where T : Foo
{
    T DoSomething<T>();
}

class Something<T> : BaseClass, ISomething<T> where T : Foo
{
    public new T DoSomething<T>()
    {
        return default(T);
    }
}

Or if you really don't want to specify Foo in the instantiation

class Program
{
    static void Main()
    {
        var something = new Something();
        var baseClass = (BaseClass)something;
        var isomething = (ISomething)something;

        var baseResult = baseClass.DoSomething<Bar>();
        var interfaceResult = isomething.DoSomething<Bar>();
        var result = something.DoSomething<Bar>();
    }
}

class Foo 
{
}

class Bar : Foo
{
}

class BaseClass
{
    public T DoSomething<T>()
    {
        return default(T);
    }
}

interface ISomething
{
    T DoSomething<T>;
}

interface ISomething<S> : ISomething where S : Foo
{
    new R DoSomething<R>() where R : Foo;
}

class Something : BaseClass, ISomething
{
    public new T DoSomething<T>()
    {
        return default(T);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top