Почему я не могу использовать совместимый конкретный тип при реализации интерфейса

Я хотел бы сделать что-то вроде этого:

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; }
    }
}

Почему компилятор жалуется..?

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>'.

Я понимаю, что интерфейс говорит IEnumerable, и класс использует List, но List - это IEnumerable.....

что я могу сделать? Я не хочу указывать IEnumerable в классе, я хочу использовать конкретный тип, который реализует IEnumerable, например List...

Ответ 1

Это проблема типового ковариации/контравариантности (см. http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#C.23).

Там обходной путь: используйте явные интерфейсы, например:

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; }
    }
}

Обратите внимание, что integers должен быть TitleCased для соответствия рекомендациям .NET.

Надеюсь, вы увидите проблему в приведенном выше коде: IList<int> совместим с IEnumerable<int> только для аксессуаров, но не для установки. Что произойдет, если кто-то называет IFoo.integers = new Qux<int>() (где Qux : IEnumerable<int>, но не Qux : IList<int>).

Ответ 2

Хотя List реализует IEnumerable, это не то, как работают интерфейсы. Интерфейс точно определяет, какие типы должны быть выставлены для свойств. Если вы создали общий интерфейс, например

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

Затем вы можете использовать IFoo<List<int>>, чтобы реализовать его так, как вы ожидаете.

Ответ 3

Вы не сможете использовать конкретный тип, если не будете делать это за кулисами. Проблема в том, что вы можете получить и установить свойство.

В вашем интерфейсе указано, что свойство имеет тип IEnumerable<int>. HashSet<int> реализует IEnumerable<int>. Это означает, что следующее должно работать нормально:

IFoo instance = new Bar();
instance.integers = new HashSet<int>();

Но так как вы пытаетесь реализовать интерфейс, используя конкретный тип List<int>, это не может работать.

Самое простое исправление, предполагая, что вам не нужно постоянно переписывать коллекцию, было бы указывать только геттер для коллекции:

public interface IFoo
{
    IEnumerable<int> Integers { get; }
}

public class Bar
{
    public List<int> Integers { get; private set; }

    public Bar(List<int> list)
    {
        Integers = list;
    }
}