-1

The following code illustrates a situation I'm having. The real code use different names and get values in other ways, but they match with what I need an answer. Specifically in lines 76-89 (the only lines I control) I need to extract a variable of type "ICollection" with the values and I don't like none of the used approaches. Is there another approach to do it without to create class "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();
        }
    }
}
Ricardo Sotolongo
  • 723
  • 11
  • 29
  • 6
    `"in lines 76-89"` -- We have zero idea what those lines are – maccettura Oct 19 '18 at 20:02
  • 1
    You cannot do what you are trying. The topic you need to study is *Covariance and Contravariance*. – CodingYoshi Oct 19 '18 at 20:11
  • Seems odd to create an instance of `AbstractCollection` at all. Anyway without far more information it´s really hard to get where speicifcally you have problems. Please provide the smallest code possible to reproduce your issue and show your input and expected output. – MakePeaceGreatAgain Oct 19 '18 at 20:19
  • 6
    This question is asked almost every day. Once more: a bowl of apples is not a bowl of fruit. Why? because you can put a banana into a bowl of fruit but cannot put a banana into a bowl of apples. A bowl of fruit is not a bowl of apples because it might already contain a banana. Therefore bowls of fruit are not type-compatible with bowls of apples. – Eric Lippert Oct 19 '18 at 20:25
  • Same thing here. Just as a bowl of a *specific* fruit is not compatible with a bowl of fruit, a collection of a *specific* entity is not compatible with a collection of entities, for the same reason. – Eric Lippert Oct 19 '18 at 20:26
  • @maccettura Lines 76-89 are those after the two lines of comments in "Context" constructor. – Ricardo Sotolongo Oct 19 '18 at 20:37
  • @HimBromBeere That is the smallest code I can provide that illustrates the whole situation. With less I can't demonstrate the point. "AbstractCollection" serves as a conversion class and I for me is also weird, so I'm looking for fresh ideas. – Ricardo Sotolongo Oct 19 '18 at 20:39
  • Remove mentioning lines in your problem context. Simply point out in the code snippet where the issue is to help readers find it quicker. They won't have to scan your entire code to try to understand you – Kevin Avignon Oct 19 '18 at 20:49
  • 2
    @EricLippert Here it's more like a bowl of tennis balls is not a bowl of apples. The collection `List` has nothing to do with `ICollection` – Fabjan Oct 19 '18 at 20:51
  • A `Collection` simply is **not** an `ICollection`. As mentioned from others you should have a look at co-variance and contra-variance. – MakePeaceGreatAgain Oct 19 '18 at 20:53
  • @Fabjan In this case "T" and "TGeneric" inherited from the same class: "Entity" and implements the same interface: "IEntity". I was expecting a smart connection. – Ricardo Sotolongo Oct 19 '18 at 20:54
  • 1
    The fact that you've created the types `ICollection` and `Collection` which are different from the system types `System.Collections.Generic` may have led to come confusion. You might want to rename to, say, `IEnumerableContainer`. Then maybe look at [c# covariant generic parameter](https://stackoverflow.com/q/6508529) and [still confused about covariance and contravariance & in/out](https://stackoverflow.com/q/3445631). If I change the definition of `ICollection` to `ICollection` then `list1` becomes non-null, see https://dotnetfiddle.net/XTzKYj – dbc Oct 20 '18 at 04:29
  • @dbc THANK YOU! Yes, probably the used names led to a confusion but you got the idea. The "magic" solution was the "out" reserved word. – Ricardo Sotolongo Oct 23 '18 at 18:30
  • @RicardoSotolongo - make that an answer then, or mark as a duplicate of [https://stackoverflow.com/q/6508529](https://stackoverflow.com/q/6508529)? – dbc Nov 03 '18 at 18:03

1 Answers1

0

Covariance guides to the solution:

interface ICollection<out TGeneric>
{
    IEnumerable<TGeneric> Items { get; }
}
Ricardo Sotolongo
  • 723
  • 11
  • 29