12

Possible Duplicate:
Does C# support return type covariance?

I'm not sure if I'm just being stupid...

If I have an interface:

public interface IMoop
{
    object Moop();
}

Why can't I implement it like so (I guess this would use implicit Covariance?)

public class MoopImplementor : IMoop
{
    string Moop();
}

Any instance of MoopImplementor would meet the contract specified by IMoop, so it seems like this should be ok.

Please enlighten me :)

EDIT: To be clear- since the implementing class returns something that inherits from the return type of the Interfaced method - I feel this should work. Specifically, a string IS an object. (and the same goes for any other inhertiance chain).

Community
  • 1
  • 1
Dave Bish
  • 19,263
  • 7
  • 46
  • 63
  • Because you are changing the semantics of the method? – leppie Apr 26 '12 at 17:01
  • You are not implementing `object Moop()`? – Niklas B. Apr 26 '12 at 17:01
  • Since string inherits from object - it does meet the contract... – Dave Bish Apr 26 '12 at 17:02
  • 1
    @Dave: The compiler is strict here. You need to make `Moop()` explicitely return an `object`, otherwise you don't implement the interface. That's not a problem, because you can simply return a `string` from the method, as you noted correctly. – Niklas B. Apr 26 '12 at 17:03
  • 1
    Technically the contract also allows me to return Integer which your implementation does not. – kailoon Apr 26 '12 at 17:03
  • 2
    Yes - I understand this. but WHY is the question :) – Dave Bish Apr 26 '12 at 17:03
  • 1
    @kailoon no it doesn't. The contract allows you to return an integer boxed as an Object, but not an integer. Those are different. – Chris Shain Apr 26 '12 at 17:04
  • @Dave: It's just the way the compiler works? What more is there to say? – Niklas B. Apr 26 '12 at 17:04
  • No, it does not meet the contract. `'MoopImplementor' does not implement interface member 'IMoop.Moop()'. 'MoopImplementor.Moop()' cannot implement 'IMoop.Moop()' because it does not have the matching return type of 'object'.` – asawyer Apr 26 '12 at 17:05
  • 2
    Regarding your edit `I feel this should work` - I feel people should give me free money, doesn't make it so. – asawyer Apr 26 '12 at 17:11
  • I mean it seems logical, obviously. – Dave Bish Apr 26 '12 at 17:16

5 Answers5

13

C# does not support return type covariance for the purposes of interface implementation or virtual method overrding. See this question for details:

Does C# support return type covariance?

C# does support generic covariance and contravariance of interfaces and delegate types that are constructed wtih reference types for the type arguments as of C# 4.

And C# does support return type covariance when converting a method that returns a reference type to a delegate type whose return type is a compatible reference type. (And similarly it supports parameter type contravariance.)

If this subject interests you, I have written a great many articles discussing various versions of variance that C# does and does not support. See

https://blogs.msdn.microsoft.com/ericlippert/tag/covariance-and-contravariance/

for details.

Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
7

Because the interface specifies that the method MUST return an object. A string may inherit from object, but the interface specifies a method that returns the much more generic type.

Keep in mind, though, there's nothing stopping you from doing the following:

public object Moop()
{
    return "Some new string";
}
Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
  • 1
    but string IS an object. – Dave Bish Apr 26 '12 at 17:04
  • @Dave: That's why we have specialized interfaces :p (I get your point in a way though) – leppie Apr 26 '12 at 17:06
  • Anywhere I use this as an interface - 'string' or anything else will meet the contract of "being an object". – Dave Bish Apr 26 '12 at 17:07
  • 1
    @Dave: You're opinion is irrelevant. It's just the way it is. What's your point? – Niklas B. Apr 26 '12 at 17:08
  • So - this is just compiler inflexibility? Because that's all the question was. – Dave Bish Apr 26 '12 at 17:10
  • @Dave: Yes, and I can see like 5 comments on this page that tell you so which you seemingly ignored? **EDIT:** I wouldn't say it's inflexibility. The standard just doesn't support this. – Niklas B. Apr 26 '12 at 17:11
  • 2
    @NiklasB. The question Dave posted is "why is it this way". I guess your saying the answer "it's just the way it is". To me that doesn't sound like a good answer. Maybe Jon Skeet will follow up as to if there is a reason for C# not allowing it. – Naz Apr 26 '12 at 17:12
  • @Naz I'd venture a guess that this is a pit-of-success issue. – asawyer Apr 26 '12 at 17:14
  • @Naz: You have a point here. I was just trying to counter the OP's repeated comments along the lines of "but it *should* be different". Yes it should be, but there's no point in repeating this phrase all over again. – Niklas B. Apr 26 '12 at 17:14
  • I'm just interested to find out the reason why if there is one. – Naz Apr 26 '12 at 17:15
  • @Naz: Yes, I'd be interested in that myself. Now let's no longer bother Justin with thousands of notifications and if necessary, continue the discussion in the comments to the question :) – Niklas B. Apr 26 '12 at 17:17
  • @Justin - I appreciate your answer - However it's not quite a deep enough description of why this isn't allowed behaviour. Cheers :) – Dave Bish Apr 26 '12 at 17:20
  • From a conceptual level, the argument should apply to method arguments. E.g. an interface with `void something(string)` should be satisfied by `void something(object)`. Of course, with the possibility of overloading, the match may become ambiguous. Probably just one of those corner cases the framework developers didn't bother themselves. – antijon Apr 26 '12 at 17:26
  • @Dave To your question of "why doesn't the compiler do behavior X?" Because the compiler team didn't design, implement test, and distribute that feature. It could have been that it was never considered, it could have been determined that it wasn't worth the effort, or it could have been technically impossible/unfeasible. The real question is, "why should the compiler implement this feature" because without a strong compelling reason to do so it won't be done, "just because it can". – Servy Apr 26 '12 at 17:47
4

A reasonable work-around:

public class MoopImplementor : IMoop {
  public string Moop() { ... }
  object IMoop.Moop() { return Moop(); }
}

This allows the public implementation on MoopImplementor to have the more accurate type while still fulfilling the requirements of the interface.

antijon
  • 982
  • 5
  • 7
  • This has always bothered me. Specifically `IEnumerable` et al. But I guess we have to do what we have to do. – leppie Apr 26 '12 at 17:09
3

You are not meeting the contract. However, you can do this with generics:

public interface IMoop<T>
{
    T Moop();
}

public class MoopImplementor : IMoop<string>
{
    public string Moop() { return ""; }
}
FishBasketGordo
  • 22,904
  • 4
  • 58
  • 91
asawyer
  • 17,642
  • 8
  • 59
  • 87
2

From the C# Language Specification:

13.4.4 Interface mapping

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.

Note that it does not say anything about inheritance or convertability! The return type must be identical.

Jacob Krall
  • 28,341
  • 6
  • 66
  • 76