yazanpro
yazanpro

Reputation: 4752

How to Cast List<T> to my own Collection

I have implemented my own collection class for various reasons. How to avoid casting failure on ItemCollection resultCollection = (ItemCollection)list;? I'm inheriting from List<T> so shouldn't I be able to cast? Can I modify my BaseEntityCollection to become able to do this?

static class Program
{
    static void Main()
    {
        ItemCollection collection = new ItemCollection();
        Item item = new Item();
        item.ID = 1;
        item.Name = "John";
        collection.Add(item);
        List<Item> list = collection.FindAll(x => x.ID == 1 && x.Name == "John");

        ItemCollection resultCollection = (ItemCollection)list; // It's breaking here
    }
}


public class ItemCollection : BaseEntityCollection<Item>
{
}

public class Item : BaseEntity
{
    public int ID { get; set; }
    public string Name { get; set; }
}

public abstract class BaseEntityCollection<T> : List<T>, IEnumerable<T> where T : BaseEntity, new()
{
}

public abstract class BaseEntity
{
}

I know that I can implement FindAllseparately on my ItemCollection But I wanted to take advantage of all the methods available on List<T>.

Also I know that I can do list.ForEach(resultCollection.Add);. But that means iterating the collection all over again which I'd like to avoid.

Upvotes: 0

Views: 510

Answers (3)

Joshua
Joshua

Reputation: 43188

ItemCollection resultCollection = new ItemCollection();
resultCollection.AddRange(collection.Where(x => x.ID == 1 && x.Name == "John"));

If by chance you don't have the AddRange extension method, make it.

void AddRange<T>(this ItemCollection c, IEnumerable<T> items) => foreach(T i in items) c.Add(i);

Upvotes: 0

Shaun Luttin
Shaun Luttin

Reputation: 141442

To augment the already great answers. You asked:

I'm inheriting from List so shouldn't I be able to cast?

Yes and no.

Your particular cast works at compile time but not at runtime.

Casting is a way of telling the compiler, "Trust me. This will work at runtime."

At runtime, we can cast from a Base class to a Descendant class only when the underlying object inside of Base is actually an object of type Descendant.

For instance, keeping in mind that string descends from object, here is an illustration of why your cast fails at runtime.

// builds but fails at runtime
object o1 = new object();
string s1 = (string)o1;     

// builds and works at runtime
// because o2 is a string in object's clothing
object o2 = (object)"";
string s2 = (string)o2;

Upvotes: 1

Ron Beyer
Ron Beyer

Reputation: 11273

Just change your constructors around so that you can initialize it with a List<Item> collection. This lets you initialize the item collection with another collection:

static class Program
{
    static void Main()
    {
        ItemCollection collection = new ItemCollection();
        Item item = new Item();
        item.ID = 1;
        item.Name = "John";
        collection.Add(item);
        List<Item> list = collection.FindAll(x => x.ID == 1 && x.Name == "John");

        ItemCollection resultCollection = new ItemCollection(list);
    }
}


public class ItemCollection : BaseEntityCollection<Item>
{
    //Allow default constructor
    public ItemCollection() { }

    //Construct with a list collection
    public ItemCollection(IEnumerable<Item> collection)
        : base(collection)
    {

    }
}

public class Item : BaseEntity
{
    public int ID { get; set; }
    public string Name { get; set; }
}

public abstract class BaseEntityCollection<T> : List<T>, IEnumerable<T> where T : BaseEntity, new()
{
    //Still be able to create blank items
    public BaseEntityCollection() { }

    public BaseEntityCollection(IEnumerable<T> collection)
        : base(collection)
    {

    }
}

public abstract class BaseEntity
{
}

In order to avoid iterating your list twice, I would change the following:

List<Item> list = collection.FindAll(x => x.ID == 1 && x.Name == "John");

to

var list = collection.Where(x => x.ID == 1 && x.Name == "John");

Which will lazy-load your list (in a sorts), but it will only iterate your collection once, when you create the new ItemCollection from it.

Upvotes: 3

Related Questions