Я хочу передать 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
{
//...
}
Вы должны сделать явное приведение - на самом деле, нет гарантии, что коллекция 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) { ... }
Интерфейс 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>
.