Reputation: 4752
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.
Upvotes: 0
Views: 510
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
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
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