Home > Software design >  How to add a generic type to a enumerable
How to add a generic type to a enumerable

Time:09-16

I want to do something like this

public IEnumerable<Foo> Foos { get; private set; }
public IEnumerable<Bar> Bars { get; private set; }

public void Add<T>(T item)
{
    if (typeof(T) == typeof(Foo))
        Foos.Append<Foo>(item);

    else if (typeof(T) == typeof(Bar))
        Bars.Append<Bar>(item);
}

Am I crazy, or is this possible?

CodePudding user response:

You can do something like this:

public IEnumerable<Foo> Foos { get; private set; }
public IEnumerable<Bar> Bars { get; private set; }

public void Add<T>(T item) where T : class
{
    if (typeof(T) == typeof(Foo))
        Foos.Append((T)item);

    else if (typeof(T) == typeof(Bar))
        Bars.Append((T)item);
}

But this is not type-safe what I assume is what you are aiming for. In particular, the check that typeof(T) == typeof(Foo) is essentially ignored by the compiler. Also, that will only work if you add the : class constraint. Otherwise, you have to convert to object first.

P.S.: Side topic, IEnumerable<T> does not have an append and extension methods will not modify the collection, so calling Append is probably pointless.

P.P.S.: Thinking more about it, I guess the statement that the compiler ignores the check is incorrect or at least up to the implementation of the JIT compiler. To my knowledge, however, the JIT compiler will only generate one implementation for all reference types and hence perform the type check at runtime and my comment still stands.

CodePudding user response:

I would suggest this solution:

public ICollection<Foo> Foos { get; private set; }
public ICollection<Bar> Bars { get; private set; }

public void Add<T>(T item) where T : class
{
    if (typeof(T) == typeof(Foo))
        Foos.Add(item as Foo);      

    if (typeof(T) == typeof(Bar))
        Bars.Add(item as Bar);
}   

You can use C# lists for working with multiple elements. Usage will be something like this:

using System.Collections.Generic;

Foos = new List<Foo>();
Bars = new List<Bar>();

var item1 = new Foo();
var item2 = new Foo();
var item3 = new Bar();
    
Add(item1);     
Add(item2);
Add(item3);

// Output length of lists or their content to check the result

P.S. If those collections and methods remain non-static object creating should be included firstly.

CodePudding user response:

To use two separate lists for the two types, use pattern matching in Add()

public class Bar 
{
}

public class Foo 
{
}

public class Storage
{
    readonly List<Foo> foos;
    readonly List<Bar> bars;

    public Storage()
    {
        foos = new List<Foo>();
        bars = new List<Bar>();
    }

    public IEnumerable<Foo> Foos { get => foos; }
    public IEnumerable<Bar> Bars { get => bars; }


    public void Add<T>(T item) where T: class
    {
        if (item is Foo foo)
        {
            foos.Add(foo);
        }
        if (item is Bar bar)
        {
            bars.Add(bar);
        }
    }
}

CodePudding user response:

I suggest to use a common ancestor interface and store the data in one list

public interface IData
{
}
public class Bar : IData
{
}

public class Foo : IData
{
}

public class Storage
{
    readonly List<IData> Data;

    public Storage()
    {
        this.Data = new List<IData>();
    }

    public IEnumerable<Foo> Foos { get => Data.OfType<Foo>(); }
    public IEnumerable<Bar> Bars { get => Data.OfType<Bar>(); }


    public void Add<T>(T item) where T: class, IData
    {
        Data.Add(item);
    }
}
  • Related