Реализация интерфейса C # - почему это не построить? - PullRequest
3 голосов
/ 23 ноября 2011

Извините, если раньше об этом спрашивали, но гуглить практически невозможно. Я думаю, что массив int реализует IEnumerable, и поэтому Thing должен иметь возможность реализовать IThing. Почему это не так?

public interface IThing
{
    IEnumerable<int> Collection { get; }
}

public class Thing : IThing
{
    public int[] Collection { get; set; }
}

обратите внимание, что

public class Thing : IThing
{
    public int[] Array { get; set; }
    public IEnumerable<int> Collection
    {
         get
         {
              return this.Array;
         }
    }
}

хорошо.

Ответы [ 7 ]

7 голосов
/ 23 ноября 2011

Чтобы интерфейс был реализован, сигнатура метода и тип возвращаемого значения должны быть идентичны, поэтому, боюсь, тот факт, что int [] можно преобразовать в IEnumerable, не имеет значения.

6 голосов
/ 23 ноября 2011

Реализация интерфейса должна точно реализовывать интерфейс.Это предотвращает возвращение типа, который реализует этот интерфейс в качестве члена.

Если вы хотите сделать это, один из вариантов - реализовать интерфейс явно :

public interface IThing
{
    IEnumerable<int> Collection { get; }
}

public class Thing : IThing
{
    public int[] Collection { get; set; }
    IEnumerable<int> IThing.Collection { get { return this.Collection; } }
}

Это позволяет вашему общедоступному API для класса использовать конкретный тип, но реализация интерфейса должна выполняться правильно.

Например, с помощью вышеприведенного вы можете написать:

internal class Test
{
    private static void Main(string[] args)
    {
        IThing thing = new Thing { Collection = new[] { 3, 4, 5 } };

        foreach (var i in thing.Collection)
        {
            Console.WriteLine(i);
        }
        Console.ReadKey();
    }
}
6 голосов
/ 23 ноября 2011

Тип возвращаемого значения свойств в разных реализациях различен - возвращение int[] отличается от возврата IEnumerable<int>.

Что касается реализации интерфейса - типы должны совпадать точно .

Это должно работать просто отлично:

public class Thing : IThing
{
    public IEnumerable<int> Collection { get; set; }
}
3 голосов
/ 23 ноября 2011

Ваши реализации должны реализовывать интерфейс именно таким, какой он есть.

2 голосов
/ 23 ноября 2011

Здесь ковариация / контравариантность может пригодиться. Эта функция позволяет вам определять входные / выходные теги на ваших шаблонах и делать следующее:

public interface IThing<out T> where T : IEnumerable<int> {
    T Collection { get; }
}

public class Thing : IThing<int[]> {
    public int[] Collection { get; set; }
}

Тогда это позволит вам определить другие реализации, а затем по-прежнему использовать их вместе как IThing<IEnumerable<int>> s.

public class Thing2 : IThing<List<int>> {
    public List<int> Collection { get; set; }
}

class Program {
    static void Main() {
        var x = new Thing();
        var y = new Thing2();
        new List<IThing<IEnumerable<int>>> { x, y };
    }
}

Преимущество этого подхода перед явной реализацией интерфейса состоит в том, что вы гарантируете , что IThing.Collection является точно таким же методом, что и Thing.Collection, тогда как в явной реализации они на самом деле являются разными методами, поэтому нет такой гарантии. Недостатком, конечно, является то, что вам нужно быть немного более явным, и это делает код немного «шумнее», чтобы на него смотреть.

Не уверен, почему компилятор C # не мог понять это неявно; я предполагаю, что это займет слишком много времени, чтобы скомпилировать эту дополнительную проверку.

2 голосов
/ 23 ноября 2011

Потому что подпись

IEnumerable<int> Collection { get; }

Не совпадает с подписью

int[] Collection { get; set; }

А при реализации интерфейса подписи должны быть точно такими же.

0 голосов
/ 23 ноября 2011

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

Хотя массив или список реализуют IEnumerable, он не является объектом IEnumerable. Это твоя проблема.

Ваша подпись должна совпадать, чтобы она работала. Вы заметите, что public List<int> Collection { get; set; } тоже не будет работать. Вам нужно будет либо изменить определение свойства вашего интерфейса, либо сделать так, чтобы ваша реализация возвращала IEnumerable <int>, как у вас во втором рабочем примере.

...