Передача коллекции абстрактного класса в качестве параметра в функцию

1

Я хочу передать ICollection<AbstractClass> в качестве параметра. Но когда я называю это коллекцией конкретных типов Visual Studio, покажите мне ошибку, которая

метод имеет некоторые недопустимые аргументы

Моя функция:

    private void GenerateId(ICollection<BaseEntity> entities)
    {
        foreach (BaseEntity e in entities)
        {
           e.Id = _baseDao.GetNextId();
        }
    }

Мой звонок:

GenerateId(entity.TitleAdmRegions);

Тип AdmRegions:

public virtual ICollection<TitleAdmRegion> TitleAdmRegions { get; set; }

И AdmRegion:

public partial class TitleAdmRegion : BaseEntity
{
  //...
}
Теги:

2 ответа

6
Лучший ответ

Вы должны сделать явное приведение - на самом деле, нет гарантии, что коллекция T, где T наследует от U, также является набором U Конечно, это, скорее всего, будет, но...

Отношение называется ковариацией - способность использовать более специфический тип в родовом "вызове" вместо своего предка. MSDN имеет хорошую статью по этой теме на С# - http://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx

Тип-безопасный способ на самом деле довольно прост с использованием дженериков:

private void GenerateId<T>(ICollection<T> entities)
  where T: BaseEntity
{
    foreach (var e in entities)
    {
       e.Id = _baseDao.GetNextId();
    }
}

Кроме того, хотя ICollection<T> не является ковариантным, IEnumerable<T> is. Таким образом, еще один простой способ - использовать IEnumerable<BaseEntity> в качестве параметра:

private void GenerateId<T>(IEnumerable<T> entities) { ... }
  • 0
    Luaan, спасибо большое! Он работает с Generics (1-й пример) и работает с IEnumerable.
  • 0
    Эта ссылка объясняет о ковариантных типах: blogs.msdn.com/b/csharpfaq/archive/2010/02/16/…
1

Интерфейс ICollection<T> не является ковариантным в T Это не может быть потому, что тип содержит такие методы, как void Add(T item). У нас есть

TitleAdmRegion - это BaseEntity

но без ковариации, это не означает, что

ICollection<TitleAdmRegion> - это ICollection<BaseEntity>

как вы, кажется, думаете. Решением является переход на интерфейс, ковариантный в аргументе типа. Вы можете использовать либо IEnumerable<out T> либо IReadOnlyCollection<out T>. Ковариация означает, что IEnumerable<TitleAdmRegion> является IEnumerable<BaseEntity>, а IReadOnlyCollection<TitleAdmRegion> является IReadOnlyCollection<BaseEntity>. Поэтому измените подпись на:

private void GenerateId(IEnumerable<BaseEntity> entities) // or IReadOnlyCollection<BaseEntity>, or IReadOnlyList<BaseEntity>, etc.
{
    foreach (BaseEntity e in entities)
    {
       e.Id = _baseDao.GetNextId();
    }
}

и все будет хорошо.

Ковариация (и контравариантность) в generics была новой в.NET 4.0 (2010). Интерфейс IReadOnlyCollection<out T> был новым в.NET 4.5 (2012). Обратите внимание, что коллекции, которые позволяют чтение и запись (например, класс List<T> и тип массива T[]), реализуют IReadOnlyCollection<out T>.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню