Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The return type of the members on an Interface Implementation must match exactly the interface definition?

Tags:

c#

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?

like image 764
Juan M. Elosegui Avatar asked Nov 03 '11 13:11

Juan M. Elosegui


3 Answers

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.

like image 53
Eric Lippert Avatar answered Oct 12 '22 04:10

Eric Lippert


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; }
}
like image 25
James Michael Hare Avatar answered Oct 12 '22 05:10

James Michael Hare


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.

like image 28
Andras Zoltan Avatar answered Oct 12 '22 03:10

Andras Zoltan