Приведение универсального типа элемента коллекции

Следующий код иллюстрирует мою ситуацию. Настоящий код использует разные имена и получает значения другими способами, но они совпадают с тем, что мне нужно ответить. В частности, в строках 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();
        }
    }
}
"in lines 76-89" - Мы понятия не имеем, что это за строки
maccettura 19.10.2018 22:02

Вы не можете делать то, что пытаетесь. Тема, которую вам нужно изучить, - Ковариация и контравариантность.

CodingYoshi 19.10.2018 22:11

Кажется странным вообще создавать экземпляр AbstractCollection. В любом случае, не имея гораздо более подробной информации, действительно трудно добраться туда, где конкретно у вас есть проблемы. Пожалуйста, предоставьте минимально возможный код, чтобы воспроизвести вашу проблему и показать ваши вводные и ожидаемые результаты.

HimBromBeere 19.10.2018 22:19

Этот вопрос задают почти каждый день. Еще раз: миска с яблоками - это не миска с фруктами. Почему? потому что вы можете положить банан в миску с фруктами, но нельзя положить банан в миску с яблоками. Миска с фруктами - это не миска с яблоками, потому что она может уже содержать банан. Поэтому чаши для фруктов несовместимы по типу с чашами для яблок.

Eric Lippert 19.10.2018 22:25

То же самое и здесь. Подобно тому, как ваза с фруктами специфический несовместима с вазой с фруктами, по той же причине набор сущностей специфический несовместим с коллекцией сущностей.

Eric Lippert 19.10.2018 22:26

@maccettura Строки 76-89 - это строки после двух строк комментариев в конструкторе «Контекст».

Ricardo Sotolongo 19.10.2018 22:37

@HimBromBeere Это самый маленький код, который я могу предоставить, который иллюстрирует всю ситуацию. С меньшим количеством я не могу продемонстрировать суть. «AbstractCollection» служит классом преобразования, и я для меня тоже странный, поэтому я ищу свежие идеи.

Ricardo Sotolongo 19.10.2018 22:39

Удалите строки упоминания в контексте вашей проблемы. Просто укажите во фрагменте кода, в чем проблема, чтобы читатели могли быстрее ее найти. Им не придется сканировать весь ваш код, чтобы попытаться понять вас.

Kevin Avignon 19.10.2018 22:49

@EricLippert Здесь больше похоже на тарелку теннисных мячей, а не тарелку с яблоками. Сборник List<T> не имеет ничего общего с ICollection<TGeneric>

Fabjan 19.10.2018 22:51

Collection<Entity> - это просто нет или ICollection<IEntity>. Как уже упоминалось от других, вам следует взглянуть на ковариацию и контра-дисперсию.

HimBromBeere 19.10.2018 22:53

@Fabjan В этом случае «T» и «TGeneric» унаследованы от одного и того же класса: «Entity» и реализуют один и тот же интерфейс: «IEntity». Я ожидал умной связи.

Ricardo Sotolongo 19.10.2018 22:54

Тот факт, что вы создали типы ICollection<TGeneric> и Collection<TGeneric>, которые отличаются от системных типов System.Collections.Generic, возможно, привел к путанице. Вы можете переименовать, скажем, в IEnumerableContainer<TGeneric>. Тогда, может быть, посмотрите Ковариантный универсальный параметр в C# и все еще не понимает ковариации и контравариантности и входа / выхода. Если я изменю определение ICollection на ICollection<out TGeneric>, тогда list1 станет ненулевым, см. dotnetfiddle.net/XTzKYj

dbc 20.10.2018 06:29

@dbc СПАСИБО! Да, наверное, использованные имена привели к путанице, но идея вы уловили. «Волшебным» решением было зарезервированное слово «out».

Ricardo Sotolongo 23.10.2018 20:30

@RicardoSotolongo - тогда ответьте или отметьте как дубликат https://stackoverflow.com/q/6508529?

dbc 03.11.2018 19:03
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
14
121
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ковариация ведет к решению:

interface ICollection<out TGeneric>
{
    IEnumerable<TGeneric> Items { get; }
}

Другие вопросы по теме