Следующий код иллюстрирует мою ситуацию. Настоящий код использует разные имена и получает значения другими способами, но они совпадают с тем, что мне нужно ответить. В частности, в строках 76-89 (единственные строки, которые я контролирую) мне нужно извлечь переменную типа «ICollection» со значениями, и мне не нравится ни один из используемых подходов. Есть ли другой способ обойтись без создания класса AbstractCollection?
namespace ConsoleApp1
{
using System.Collections.Generic;
using System.Linq;
interface IEntity
{
string Id { get; }
string Name { get; }
}
class Entity : IEntity
{
public Entity(string id, string name)
{
Id = id;
Name = name;
}
public string Id { get; }
public string Name { get; }
}
interface ICollection<TGeneric>
{
IEnumerable<TGeneric> Items { get; }
}
class Collection<TGeneric> : ICollection<TGeneric> where TGeneric : Entity, IEntity
{
public IEnumerable<TGeneric> Items { get; set; }
}
class AbstractCollection<TConcrete, TAbstract> : ICollection<TAbstract> where TAbstract : class, IEntity
{
public AbstractCollection(ICollection<TConcrete> collection)
{
this._Items = new List<TAbstract>();
if (collection?.Items != null)
{
foreach (TConcrete concreteItem in collection.Items)
{
TAbstract abstractItem = concreteItem as TAbstract;
this._Items.Add(abstractItem);
}
}
}
public IEnumerable<TAbstract> Items
{
get { return this._Items; }
set { this._Items = value?.ToList(); }
}
private IList<TAbstract> _Items { get; set; }
}
class EntityCollection : Collection<Entity>
{
public EntityCollection()
{
var items = new List<Entity>()
{
new Entity("1", "Name1"),
new Entity("2", "Name2"),
new Entity("3", "Name3")
};
Items = items;
}
}
class Context
{
public Context()
{
var concreteItems = new EntityCollection();
// I can modify from this line to the end of the method but not any code before.
// I expected values in "list1" but is null.
var list1 = concreteItems as ICollection<IEntity>;
var list2 = concreteItems as ICollection<Entity>;
var abstractItems = new List<IEntity>();
foreach (Entity concreteItem in concreteItems.Items)
{
IEntity abstractItem = concreteItem as IEntity;
abstractItems.Add(abstractItem);
}
// Why "list3" is null?
var list3 = abstractItems as ICollection<IEntity>;
// I want to avoid class "AbstractCollection"
var list4 = new AbstractCollection<Entity, IEntity>(list2);
// Finally "list5" has value in the way I want it.
var list5 = list4 as ICollection<IEntity>;
}
}
class Program
{
static void Main(string[] args)
{
var context = new Context();
}
}
}
Вы не можете делать то, что пытаетесь. Тема, которую вам нужно изучить, - Ковариация и контравариантность.
Кажется странным вообще создавать экземпляр AbstractCollection. В любом случае, не имея гораздо более подробной информации, действительно трудно добраться туда, где конкретно у вас есть проблемы. Пожалуйста, предоставьте минимально возможный код, чтобы воспроизвести вашу проблему и показать ваши вводные и ожидаемые результаты.
Этот вопрос задают почти каждый день. Еще раз: миска с яблоками - это не миска с фруктами. Почему? потому что вы можете положить банан в миску с фруктами, но нельзя положить банан в миску с яблоками. Миска с фруктами - это не миска с яблоками, потому что она может уже содержать банан. Поэтому чаши для фруктов несовместимы по типу с чашами для яблок.
То же самое и здесь. Подобно тому, как ваза с фруктами специфический несовместима с вазой с фруктами, по той же причине набор сущностей специфический несовместим с коллекцией сущностей.
@maccettura Строки 76-89 - это строки после двух строк комментариев в конструкторе «Контекст».
@HimBromBeere Это самый маленький код, который я могу предоставить, который иллюстрирует всю ситуацию. С меньшим количеством я не могу продемонстрировать суть. «AbstractCollection» служит классом преобразования, и я для меня тоже странный, поэтому я ищу свежие идеи.
Удалите строки упоминания в контексте вашей проблемы. Просто укажите во фрагменте кода, в чем проблема, чтобы читатели могли быстрее ее найти. Им не придется сканировать весь ваш код, чтобы попытаться понять вас.
@EricLippert Здесь больше похоже на тарелку теннисных мячей, а не тарелку с яблоками. Сборник List<T> не имеет ничего общего с ICollection<TGeneric>
Collection<Entity> - это просто нет или ICollection<IEntity>. Как уже упоминалось от других, вам следует взглянуть на ковариацию и контра-дисперсию.
@Fabjan В этом случае «T» и «TGeneric» унаследованы от одного и того же класса: «Entity» и реализуют один и тот же интерфейс: «IEntity». Я ожидал умной связи.
Тот факт, что вы создали типы ICollection<TGeneric> и Collection<TGeneric>, которые отличаются от системных типов System.Collections.Generic, возможно, привел к путанице. Вы можете переименовать, скажем, в IEnumerableContainer<TGeneric>. Тогда, может быть, посмотрите Ковариантный универсальный параметр в C# и все еще не понимает ковариации и контравариантности и входа / выхода. Если я изменю определение ICollection на ICollection<out TGeneric>, тогда list1 станет ненулевым, см. dotnetfiddle.net/XTzKYj
@dbc СПАСИБО! Да, наверное, использованные имена привели к путанице, но идея вы уловили. «Волшебным» решением было зарезервированное слово «out».
@RicardoSotolongo - тогда ответьте или отметьте как дубликат https://stackoverflow.com/q/6508529?





Ковариация ведет к решению:
interface ICollection<out TGeneric>
{
IEnumerable<TGeneric> Items { get; }
}
"in lines 76-89"- Мы понятия не имеем, что это за строки