Приведение пользовательских коллекций к реализованным интерфейсам - PullRequest
1 голос
/ 15 апреля 2020

В следующем, почему Todos1 работает, а Todos2 нет? Как заставить это работать?

class Program
{
    static void Main(string[] args)
    {
        _todos = new CustomCollection<Todo>();
    }

    private static CustomCollection<Todo> _todos;

    public static IEnumerable<ITodo> Todos1
    {
        get { return _todos; }
    }

    public static ICustomCollection<ITodo> Todos2
    {
        get { return _todos; }
    }

    public class CustomCollection<T> : Collection<T>, ICustomCollection<T>
    {
    }

    public interface ICustomCollection<T> : IEnumerable<T>
    {
    }

    public interface ITodo
    {
    }

    public class Todo : ITodo
    {
        public string Description { get; set; }
    }
}

Ответы [ 2 ]

2 голосов
/ 15 апреля 2020

Так работает дисперсия; IEnumerable<T> на самом деле IEnumerable<out T>, что означает ковариант ; это означало, что все, что является IEnumerable<Todo>, также IEnumerable<ITodo>, потому что любое Todo является ITodo.

Однако коллекции / списки / et c не ковариантный (или контравариантный); так что здесь нет явной кастуемости. Причина в том, что:

  • у вас есть CustomCollection<Todo>
  • , если его можно преобразовать в CustomCollection<ITodo>, вы можете Add любой ITodo
  • , включая class SomethingElse : ITodo, что не a Todo
  • , поэтому в вашей коллекции будет не Todo Todo s

Компилятор защищает вас!

1 голос
/ 15 апреля 2020

Вы должны объявить ваш ICustomCollection<T> интерфейс как ковариантный

public interface ICustomCollection<out T> : IEnumerable<T>
{
}

В противном случае он инвариантен и вы можете привести его только к тому же типу, который использовался для объявления, который был Todo классом, а не ITodo interface.

IEnumerable<T> уже имеет ковариантный параметр типа generi c T, поэтому первое свойство работает, как и ожидалось.

...