简体   繁体   中英

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 FindAll separately 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.

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.

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;
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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM