Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I use a compatible concrete type when implementing an interface

I would like to be able to do something like this :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test
{
    public interface IFoo
    {
        IEnumerable<int> integers { get; set; }
    }

    public class Bar : IFoo
    {
        public List<int> integers { get; set; }
    }
}

Why does the compiler complains..?

Error   2   'Test.Bar' does not implement interface member 'Test.IFoo.integers'. 'Test.Bar.integers' cannot implement 'Test.IFoo.integers' because it does not have the matching return type of 'System.Collections.Generic.IEnumerable<int>'.

I understand that the interface says IEnumerable and the class uses a List, but a List is an IEnumerable.....

what can I do? I do not want to specify IEnumerable in the class, I want to use a concrete type that implements IEnumerable, like List...

like image 536
Bruno Avatar asked Mar 19 '13 02:03

Bruno


2 Answers

Although List implements IEnumerable that's not the way interfaces work. The interface specifies exactly which types need to be exposed for properties. If you created a generic interface like

public interface IFoo<T> where T : IEnumerable<int>
{
    T integers { get; set; }
}

You could then use IFoo<List<int>> to implement it in the way you expect.

like image 190
Jake Anderson Avatar answered Oct 12 '22 05:10

Jake Anderson


This is a Type Covariance/Contravariance issue (see http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#C.23 ).

There's a workaround: use explicit interfaces, like so:

public class Bar : IFoo {

    private IList<int> _integers;

    IEnumerable<int> IFoo.integers {
        get { return _integers };
        set { _integers = value as IList<int>; }
    }

    public IList<int> integers {
        get { return _integers; }
        set { _integers = vale; }
    }
}

Note that integers should be TitleCased to conform to .NET's guidelines.

Hopefully you can see the problem in the code above: IList<int> is compatible with IEnumerable<int> only for the accessor, but not for setting. What happens if someone calls IFoo.integers = new Qux<int>() (where Qux : IEnumerable<int> but not Qux : IList<int>).

like image 13
Dai Avatar answered Oct 12 '22 05:10

Dai