12

According to CSharp Language Specification.

An interface defines a contract that can be implemented by classes and structs. An interface does not provide implementations of the members it defines—it merely specifies the members that must be supplied by classes or structs that implement the interface.

So I a have this:

interface ITest
{
    IEnumerable<int> Integers { get; set; }
}

And what I mean is. "I have a contract with a property as a collection of integers that you can enumerate".

Then I want the following interface Implementation:

class Test : ITest
{
    public List<int> Integers { get; set; }
}

And I get the following compiler error:

'Test' does not implement interface member 'ITest.Integers'. 'Test.Integers' cannot implement 'ITest.Integers' because it does not have the matching return type of 'System.Collections.Generic.IEnumerable'.

As long as I can say my Test class implement the ITest contract because the List of int property is in fact an IEnumerable of int.

So way the c# compiler is telling me about the error?

3
  • 1
    The signature is not the same. If you are implementing the interface, you need to implement it the same (with exception of covariant/contravariant generic type parameters). Commented Nov 3, 2011 at 14:02
  • Ditto what James said IEnumerable<int> != List<int>
    – NotMe
    Commented Nov 3, 2011 at 14:05
  • If you only wanted the "get" it should in theory be possible. See Eric Lippert's post below on how this can be achieved in c#. In some cases can also just make two methods one called IntegerList and one called IntegerEnumerable.. and then the IntegerEnumerable implementation calls the other method internally.
    – Gregor
    Commented Dec 8, 2014 at 21:18

6 Answers 6

20

FYI, the feature you want is called "virtual method return type covariance", and as you have discovered, it is not supported by C#. It is a feature of other object-oriented languages, like C++.

Though we get requests for this feature fairly frequently, we have no plans to add it to the language. It is not a terrible feature; if we had it, I'd use it. But we have many reasons not to do it, including that it is not supported by the CLR, it adds new and interesting failure modes to versionable components, Anders does not think it is a very interesting feature, and we have many, many higher priorities and a limited budget.

Incidentally, though people ask us for virtual method return type covariance all the time, no one ever asks for virtual method formal parameter type contravariance, even though logically they are essentially the same feature. That is, I have a virtual method/interface method M that takes a Giraffe, and I would like to override it/implement it with a method M that takes an Animal.

8
  • 3
    @RomanR. I assume he means Anders Hejlsberg
    – Brian
    Commented Nov 3, 2011 at 15:36
  • 5
    virtual method formal parameter type contravariance is just too many words. People only ask for things they can describe easily :) Commented Nov 3, 2011 at 17:32
  • 2
    @Jason: In the given example, you are correct; the get/set property cannot be safely made covariant in its type. However, if there were only a getter, and its return type was known to be a reference type, then it could be safely made covariant for the purposes of implementation. (And similarly, if it were a set-only property of reference type then it could be safely made contravariant.) If we supported the feature, which we do not. Commented Nov 3, 2011 at 20:19
  • 1
    @Eric Lippert: Thank you, that makes sense. So there actually isn't a feature that he "wants" here, his particular problem can't be solved in this case.
    – jason
    Commented Nov 3, 2011 at 20:26
  • 2
    @fuglede: I haven't worked on the C# team for over four years; request it of them on GitHub! Commented Apr 5, 2017 at 13:29
10

You can't do this because you'd have a major problem on your hand depending on the implementation if this were allowed. Consider:

interface ITest
{
    IEnumerable<int> Integers { get; set; }
}

class Test : ITest
{
    // if this were allowed....
    public List<int> Integers { get; set; }
}

This would allow:

ITest test = new Test();
test.Integers = new HashSet<int>();

This would invalidate the contract for Test because Test says it contains List<int>.

Now, you can use explicit interface implementation to allow it to satisfy both signatures depending on whether it's called from an ITest reference or a Test reference:

class Test : ITest
{
    // satisfies interface explicitly when called from ITest reference
    IEnumerable<int> ITest.Integers
    {
        get
        {
            return this.Integers; 
        }
        set
        {
            this.Integers = new List<int>(value);
        }
    }

    // allows you to go directly to List<int> when used from reference of type Test
    public List<int> Integers { get; set; }
}
6

Simple fact is, if an interface says:

IInterface{
   Animal A { get; }
}

Then an implementation of that property must match the type exactly. Trying to implement it as

MyClass : IInterface{
  Duck A { get; }
}

Does not work - even though Duck is an Animal

Instead you can do this:

MyClass : IInterface{
  Duck A { get; }
  Animal IInterface.A { get { return A; } }
}

I.e. provide an explicit implementation of the IInterface.A member, exploiting the type relationship between Duck and Animal.

In your case this means implementing, the getter at least, ITest.Integers as

IEnumerable<int> ITest.Integers { get { return Integers; } }

To implement the setter, you will need to cast optimistically or use .ToList() on the input value.

Note that the use of A and Integers inside these explicit implementations is not recursive because an explicit interface implementation is hidden from the public view of a type - they only kick in when a caller talks to the type through it's IInterface/ITest interface implementation.

4

You need 13.4.4 from the specification:

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

A and B are properties, the name and type of A and B are identical, and A has the same accessors as B (A is permitted to have additional accessors if it is not an explicit interface member implementation).

Additionally, your belief that List<int> Integers { get; set; } satisfies the contract of IEnumerable<int> Integers { get; set; } is false. Even if the specification were somehow relaxed to not require that the return types be identical, note that a property of type List<int> with a public setter is not anywhere near the same as a property of type IEnumerable<int> with a public setter because to the latter you can assign an instance of int[], but to the former you can not.

0

You can do something like this:

 interface ITest
{
    IEnumerable<int> Integers { get; set; }
}

class Test : ITest
{
    public IEnumerable<int> Integers { get; set; }

    public Test()
    {
        Integers = new List<int>();
    }
}
-6

Because Test is not an ITest. Why ? With an ITest you can set an array to the property Integers. But you can't with a Test. With .net 4.0 you can do things like that (covariance and contra-variance) but not exactly that, it's incorrect in every language.

7
  • You are mistaking covariance for generic type assignability with covariance for virtual return types; though similar, they are different kinds of covariance. C# 4 supports generic type covariance but not return type covariance. Commented Nov 3, 2011 at 14:35
  • No no, I'm not confusing. I explain him why in his case its not covariance and not contra-variance, it's incorrect. (Please Eric never teach me covariance and contravariance, these terms come from french mathematicians, we know how to use them and what they really mean)
    – Toto
    Commented Nov 3, 2011 at 14:37
  • 2
    The original poster wishes to use virtual method return type covariance. That is rather different than generic type assignment compatibility covariance. I'm pointing out that it is confusing to say that .NET 4.0 lets you "do things like that", because it is not clear whether the antecedant of "things like that" is virtual method return type covariance or generic type assignment compatibility covariance. Commented Nov 3, 2011 at 14:48
  • But if you specify the return type as a parameter type to the generic ? like in MyType<ReturnType : ...> ?
    – Toto
    Commented Nov 3, 2011 at 14:50
  • Also, it is not incorrect in every language. There are languages which permit non-type-safe covariant and contravariant operations. C# and Java permit unsafe array covariance. Eiffel supports virtual method formal parameter type covariance, oddly enough, but Eiffel has an unusual type system. Commented Nov 3, 2011 at 14:51

Not the answer you're looking for? Browse other questions tagged or ask your own question.