2

I have a concrete class implementing an interface with method signature

IEnumerable<T> Foo()

with

List<T> Foo()

The compiler says List<T> Foo cannot implement IEnumerable<T> Foo. Why?

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
DiscoDirk
  • 21
  • 1
  • Can we see an example? What is a concrete class? Is it made of rock? – gunr2171 Jun 13 '14 at 18:33
  • I see little technical reasons for that, since the conversion is representation preserving and typesafe. In fact very similar conversions happen with interface covariance or delegate covariance. But the C# specification forbids implicit interface implementation unless there is a precise signature match. The reasons for this decision are not clear, but we can default to "there were higher priority features and it wasn't considered to be worth their time". – CodesInChaos Jun 13 '14 at 18:36
  • As a workaround, you can use explicit interface implementation. `IEnumerable IFoo.Foo() { return Foo(); }` – CodesInChaos Jun 13 '14 at 18:36
  • 2
    This question appears to be off-topic because it is about why the C# developers choose not to take the time to invest in a particular feature. – Servy Jun 13 '14 at 18:38
  • @Servy This question (in it's simplicity) seems to be asking for why the compiler forbids it, not necessarily about the rationale that influenced such a decision. The reason is because the C# specification says so. – user2864740 Jun 13 '14 at 18:43
  • 1
    Why? Because it wasn't designed that way. Questions asking why certain design decisions were made in some product are not typically productive here. – Jim Mischel Jun 13 '14 at 18:45
  • @user2864740 That's essentially just begging the question. – Servy Jun 13 '14 at 18:45
  • I think that *a* correct answer - and I do believe that there *is* one here - is to quote the relevant C# specification. This would also add to the knowledge contained on SO and address the OP's *direct* question. It's a shame to see such a question closed merely on pedantic reasons and speculation, especially when such have not been confirmed or denied by the OP. (Although I suspect there are duplicates.) – user2864740 Jun 13 '14 at 18:46
  • @user2864740 I fail to see how an answer stating, "the code doesn't compile because the specs say it shouldn't" is adding value. Presumably he knows that the specs say it shouldn't compile. The only reason it wouldn't is if its a compiler bug. That answer could be given to every single question that asks why the code doesn't compile. Its not adding value. – Servy Jun 13 '14 at 18:51
  • "Presumably.." - also, value added is not merely for the sake of the OP (or people who answer). If such was the case, every question should be immediately deleted after it was answered. (Or, we can still be less hasty on the close and let someone take time to write an expanded answer exploring different details .. I *do* appreciate the fact that this question wasn't down-voted though, good restraint ;-) – user2864740 Jun 13 '14 at 18:52
  • @user2864740 Why would knowing that you can't do something because you're not allowed to do that something be any more useful to any future visitor than for the OP? As I said, you're proposed "answer" is just begging the question. It provides no useful information. – Servy Jun 13 '14 at 19:02
  • 1
    Related question about return type covariance: [C# Covariance on subclass return types](http://stackoverflow.com/questions/9235877/c-sharp-covariance-on-subclass-return-types) – CodesInChaos Jun 13 '14 at 19:18
  • Dupe: [does-c-sharp-support-return-type-covariance](http://stackoverflow.com/questions/5709034/does-c-sharp-support-return-type-covariance) – nawfal Jul 10 '14 at 06:23

3 Answers3

2

Why does the compiler reject this?

Because the specification does not allow this:

For purposes of interface mapping, a class member A matches an interface member B when:

  • A and B are methods, and the name, type, and formal parameter lists of A and B are identical.

  • [...]

(Quoted from the C# 5.0 specification - 13.4.4 Interface mapping)

Are there any technical hurdles if one were to add this feature to the specification?

Not that I know of:

  • Since both types are reference types, they're representation preserving. Thus there is binary compatibility between those signatures.
  • Covariance on return types is typesafe, since a method that returns List<T> returns the IEnumerable<T> expected by callers of the interface

Are there similar features already in C# and .NET?

Yes. .NET 4 supports:

  • Covariance on delegate return types: Func<List<T>> is a Func<IEnumerable<T>>
  • Interface covariance: IEnumerable<List<T>> is an IEnumerable<IEnumerable<T>

This provides further evidence for the feasiblity of the feature.

Why isn't it supported then?

Only a member of the C# team can answer that definitely.

But in the absence of further evidence we can assume:

Every feature has a cost to specify, implement test and maintain. So the developers can only implement some features. They'll choose feature which offer a big gain for little work. So there were probably higher priority features and it wasn't considered to be worth their time.

Are there workarounds?

Just use explicit interface implementation:

class FooClass<T> : IFoo
{
    public List<T> Foo()
    {
         //do something
    }

    IEnumerable<T> IFoo.Foo()
    {
        return Foo();
    }
}
Community
  • 1
  • 1
CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • Would there any practical workaround if the method were abstract rather than being an interface method, short of declaring an abstract "dummy" class which inherits from the base chains the abstract method to one with a different name, thus allowing a class which derives from the "dummy" class to implement the abstract method and define a `new` method with a different return type? Would any difficulty with that be imposed by C# or the CIL spec? – supercat Jun 13 '14 at 20:36
  • @supercat 1) Changing the spec to add return type covariance: This looks trickier that adding covariance for interface implementation. I think the consequences for the C# spec wouldn't be too severe, but adapting the CLR to use different matching rules for overriding could be problematic. 2) Working around: I know no better technique than having one protected virtual method and a public shadowed method, like you suggest. Personally I avoid public virtual in general, doing parameter validation in the public non virtual method and implementing the overridable functionality in a protected method. – CodesInChaos Jun 13 '14 at 20:51
  • Is there any way to avoid the extra layer of inheritance? I can certainly appreciate that there are often advantages to having public non-virtual methods which chain to virtual or abstract methods in cases where the base-class contract would permit the parent class to add behavior, but conceptually if a sub-derived class needs to add functionality, that should be handled by having the first derived class make its implementation of the base class virtual method `final` and declare its own virtual method for subclasses to override. Having to use new names at each stage seems a bit icky. – supercat Jun 13 '14 at 21:06
  • I think a big part of the problem comes from the fact that .NET assumes the face a class exposes to public should be a subset of the face it exposes to derived-class objects, but there are times that really shouldn't be true. A concrete class may have a constructor or virtual method implementation which cannot be correct for any derived class other than itself, and may have access to protected members which sub-derived classes cannot possibly use correctly (e.g. `MemberwiseClone`). I'm not sure what research has been done into ways of defining the faces separately. – supercat Jun 13 '14 at 21:10
1

Because you cannot change the return type or any other type in the signature of the method you are implementing in C#.

The language specification for C# says so. Why they made this decision is unknown, but as a wild guess, they probably thought that the potential gain is not worth the cost.

What you can do however is keep the signature as IEnumerable<T> and simply return a List<T>.

nvoigt
  • 75,013
  • 26
  • 93
  • 142
  • 2
    This is simply incorrect. Return type covariance in no way breaks static typing. It's a feature in Java, for example. It's simply a feature C# chose not to spend the time/money to add. There is no real technical barrier. – Servy Jun 13 '14 at 18:39
  • 2
    You cannot in C#. That's the simple truth even 89K points have to accept. Could you do, if the implementers of C# made different decisions? Sure. But that was not the question, was it? – nvoigt Jun 13 '14 at 18:40
  • @Servy I'd consider return type covariance and covariance on implicit interface implementation to be different, albeit closely related features. – CodesInChaos Jun 13 '14 at 18:41
  • That's *exactly* what the question is. Right in the question he makes it clear that he knows that this won't compile. He *knows* that it's not a part of the language. He wants to know why it's not a part of the language. – Servy Jun 13 '14 at 18:42
  • @CodesInChaos You are correct, although it's too late for me to edit my comment. – Servy Jun 13 '14 at 18:46
0

I can't answer the question of why, but it may be worth noting that this limitation has very little practical impact, since explicit interface implementation allows you to get the desired effect with a small amount of glue code:

public interface IFooable<T>
{
    IEnumerable<T> Foo();
}

public sealed class Fooable<T> : IFooable<T>
{
    public List<T> Foo()
    {
        //...
    }

    IEnumerable<T> IFooable<T>.Foo()
    {
        return Foo();
    }
}
Dan Bryant
  • 27,329
  • 4
  • 56
  • 102