C#; InvalidCastException, what´s wrong? - c#

I want to cast the Objects of an List to it´s Subclass.
For that I used Casting populated List<BaseClass> to List<ChildClass>
Now, when I execute the Code I always get an InvalidCastException in System.Core.dll ""The Object Item cannot be cast to SubItem "
[Serializable]
public class SubItem: Item
{
public SubItem(int id) : base( id)
{
}
}
public getStuff(int id){
//where the Error is thrown
//Inventory has Items in it, furthermore atm Inventory is definitely not empty, but could be later on
var items = Inventory.FindAll(x=> x.id==id).Cast<SubItem>().ToList();
}
Item is from an API, I am not able to change anything there.
Item inherits from IEquatable.
The LINQ is definitely working, but when I am casting I get the Error. What do i do wrong?

It could be that some of the items in your list are not of the derived class type, you should be able to see this in the debugger. Also as someone has pointed out you may have to pull the data back from the database before doing the cast, the linq provider may not be capable of doing that for you.
So you could try either of the following:
Filter out items that are not of SubItem
var items = Inventory.FindAll(x=> x.id==id).OfType<SubItem>().ToList();
Pull the data back from the database before casting
var items = Inventory.FindAll(x=> x.id==id).ToList().Cast<SubItem>().ToList();
Or a combination of the two if the linq provider cannot do the OfType
var items = Inventory.FindAll(x=> x.id==id).ToList().OfType<SubItem>().ToList();

I am going to assume that your property Inventory is strongly typed collection/list/IEnumerable of generic type Item (example: ICollection<Item>).
The InvalidCastException will occur when calling Cast if one or more items is not of the type specified in the generic argument. See details in the Microsoft page Enumerable.Cast. To get around that you need to only retrieve items of that type which will execute the cast on them during the filter. This can be done with Enumerable.OfType. Your code then becomes:
var items = Inventory.FindAll(x=> x.id==id).OfType<SubItem>().ToList();
From the documentation Enumerable.Cast
If an element cannot be cast to type TResult, this method will throw an exception. To obtain only those elements that can be cast to type TResult, use the OfType method instead of Cast(IEnumerable).
Edit based on these comments
Established that all items returned are of type Item and not SubItem. This explains the exception.
... said that the List consists of Item that´s why I want to downcast them to SubItem.
Your inheritance hierarchy as you have posted it in your question is System.Object -> Item -> SubItem. (I include Object as I am assuming we are dealing with reference types and also everything eventually inherits from System.Object). What this means is that a SubItem could be cast to an Item or an System.Object, an Item instance could be cast to a System.Object. However the inverse is not true, you cant cast up the graph. An instance of System.Object cannot become an Item and an Item cannot become a SubItem.
Having a generic List<Item> is fine, this could contain types SubItem or Item or anything that derives from Item. But again, you cannot cast an instance of Item to SubItem.
If this is not clear then I recommend you do some reading up on inheritance as well as Polymorphism which is a different topic but also very important if you want to know why inheritance is used and what makes it such a great tool for abstraction.

Assuming that Inventory contains a list of "Item" and you want to get a list of "SubItem" replace your line with this:
var items = Inventory.Where(x => x.id == id).Select(x => new SubItem(x.id)).ToList();
So the first part is going to get you the items that meet your conditions and the second part will create a new SubItem for each one and give you a list of them.

Related

Why ListItem must be declared explicitly in foreach C#

I'm confused as to why this code would not compile if listControl is a ListControl object such as a DropDownList:
foreach (var item in listControl.Items) {
item.Value = string.empty;
}
The compiler considers item to be of type object. It works if I replace var with ListItem, declaring the variable explicitly. The Items property is a ListItemCollection, which implements IEnumerable. Shouldn't the compiler be able to tell that the objects in the collection are of type ListItem?
This is a strange behavior since, as you point out, ListItemCollection is a collection of ListItems. This appears related to the fact that this was implemented before C# supported generics. And so it implements IEnumerable instead of IEnumerable<ListItem>, and isn't able to determine the correct type.
I suggest rewriting your loop this way:
foreach (ListItem item in listControl.Items) {
item.Value = string.empty;
}
Check out this question for additional information.
Compiler can't tell that to you at compile time because the items are object. It will compile with any type, for example if you use
foreach(int item in collection)
it will compile, because casting from object to int is valid, but it will throw an exception at runtime because the types are not compatible.
Original Question
The Items property is a ListItemCollection, which implements IEnumerable. Shouldn't the compiler be able to tell that the objects in the collection are of type ListItem?
Short Answer
No. The compile-time type is object because that's the compile-time type of IEnumerator.Current. The var keyword is still statically typed! If ListItemCollection implemented IEnumerable<T> instead of IEnumerable, then the compiler would know more.
More Details
From the docs:
... a System.Collections.IEnumerator [is] used to get items from the collection.
When you use a foreach loop, you're implicitly using the IEnumerator interface. So, when you access each item, the Current property returns an object even though it's a ListItem underneath at run-time.
Importantly, the var keyword is still static, so you might as well have written the following code. Would you expect the compiler to figure out this:
foreach (var item in (object)listControl.Items) {
item.Value = string.empty;
}
Your var is a statically-typed object. If you want the run-time to figure out that it's a ListItem, then you have to use dynamic instead of var.

why does type inference break with SelectedItemCollection?

The ASPXCheckBoxList has a collection called 'SelectedItems' of type 'SelectedItemCollection' (it implements ICollection, IEnumerable)
for some reason, there's a difference between this:
foreach (ListEditItem si in cblCommonJointOwnership.SelectedItems)
{
//do stuff and have si.Value and si.Text and si.Foo
}
and this:
foreach (var si in cblCommonJointOwnership.SelectedItems)
{
//seems that si is just a plain object here...
}
I realize this isn't DevExpress specific.. so I suppose a general question: why does the 'var' version become inferred as an object instead of a ListEditItem?
Thanks
SelectedItemCollection implements IEnumerable but not IEnumerable<T>. Because of that, the compiler doesn't know anything about the type stored in the collection and has to assume the most generic type (which is Object).
Your first statement specifies the type. This actually will result in a cast of each item in the collection back to your specified type and would fail if any of the elements in the collection couldn't be properly cast.
Because SelectedItems is a specialized collection. It's not generic either. This makes it impossible for type inference to operate.
Put another way, the objects in that list could be anything; even of different types. Therefore, the collection doesn't provide any typed result.

Converting List<object> to List<complex> where complex is a user-defined type

So lets say I have a class like
public class Complex { } // this is what I mean when I say List<Complex>
// its just a user-defined class or type if you will
Now let's say I have a method which returns a list that holds objects.
Now let's say I can further guarantee that each of these objects in the list is actually of type Complex (in other words List < Complex >)
I would like the most painless way of casting this list as possible. A one-liner would be ideal, but a few lines would be fine too.
Here is what I tried (but doesn't work - InvalidCastOperation exception):
// Sorry this is so hard to read! Also .Cast<Complex>() doesn't work either :(
return (ComplexCollection) ((List<Complex>) ((List<object>) complexElementsDictionary["ComplexElementCollection"]).OfType<Complex>());
Some specifics about this code snippet:
ComplexCollection inherits List<Complex>
complexElementsDictionary is of type (Dictionary<string, List<object>)
So in plain terms I'm asking a Dictionary<> for its corresponding list of objects. Then I try to cast this list to what its supposed to be, which is a list of Complex.
Note - Please try to ignore the fact that I am casting like this at runtime. I'm deserializing an XML collection (in app.config actually) and this is how I've chosen to do it. There may be a better way, but for now I just want to see if anyone knows a way to do what I'm asking.
Thank you so much! I hope it is clear what I'm asking, and if not I'll update the question.
Breaking up your example code and modifying it slightly to use Cast():
List<object> list = complexElementsDictionary["ComplexElementCollection"];
List<Complex> typedList = list.Cast<Complex>().ToList();
You now have a list of objects of the required type. But what you actually want is an instance of your collection object, and this cannot be obtained by another cast. That is, you can't do this:
ComplexCollection collection = (ComplexCollection)typedList;
because typedList simply isn't an instance of ComplexCollection and there is no conversion operator available to the compiler to convert from List<Complex> to ComplexCollection.
You will need to create your instance and add the Complex objects to it:
ComplexCollection collection = new ComplexCollection();
foreach(Complex c in typedList){
collection.Add(c);
}
Or in just four lines:
ComplexCollection collection = new ComplexCollection();
foreach(Complex c in complexElementsDictionary["ComplexElementCollection"].Cast<>(Complex)){
collection.Add(c);
}
To convert a List<object> to List<Complex> is as simple as:
List<object> objectList = complexElementsDictionary["ComplexElementCollection"];
List<Complex> complexList = objectList.Cast<Complex>().ToList();
However you can't cast a List<Complex> to ComplexCollection just because ComplexCollection inherits from List<Complex>. (the other way around is fine though)
List<T> has a constructor which takes an IEnumerable<T> and adds the elements to a list. So I think what you need is to add this constructor to ComplexCollection:
public ComplexCollection(IEnumerable<Complex> values) : base(values)
{
/* plus any other logic you have */
}
And then your example with the return statement becomes:
return new ComplexCollection(
complexElementsDictionary["ComplexElementCollection"].Cast<Complex>()
);
To convert a list to a new type you can just do something as simple as
(ComplexCollection)(yourList.Select(x => (complexType)x).ToList());
That should work, basically your going through your list of objects in the dictionary and casting each item to the appropriate type, then converting that to a list and then converting your list to the more specific collection type.
I'm afried there's no direct solution. Maybe you could use ConvertAll:
// ol is of type List<Object>
List<Complex> cl = ol.ConvertAll(o=>(Complex)c);

foreach-ing through a listview and accessing subitems?

I'm having difficulty using a foreach statement with a WinForm ListView control. The following two code blocks demonstrates what I'm trying to do. It works with a for loop, but not a foreach.
foreach(var item in listView.Items){
item. <-Can't access any of the subitems of this item
}
vs
for(int i=0;i<listView.Items.Count;i++){
listView.Items[i].Subitems[1] <- Here I can access the sub items
}
I'm trying to use a foreach loop so I can more easily remove items from the ListView.
You need to specify the type:
foreach(ListViewItem item in listView.Items){
To answer your comments:
This is because most controls' item collections implement the non-generic ICollection (and IEnumerable), see this MSDN entry for ListViewItemCollection for example. Since it doesn't implement the generic ICollection<T> or IEnumerable<T>, the compiler can't guess the type of the items from looking at the collections themselves, so you have to tell it that they're of type ListViewItem instead of using var.
You need to specify the type if the item in the collection explicitly. The var keyword uses type inference in order to determine the type of the variable. In the case of var in a foreach clause, it uses the particular implementation of IEnumerable to determine the type.
If the collection only implements IEnumerable (and not a generic IEnumerable<T>), then var will be object
If the collection implements one generic IEnumerable<T> (say, IEnumerable<int>), then var will be T (in the example here, var would be int)
In your case, ListViewItemCollection does not implement any generic form of IEnumerable<T>, so var is assumed to be object. However, the compiler will allow you to specify a more specific type for the iterator variable if the enumerable only implements IEnumerable, and it automatically inserts a cast to that particular type.
Note that, because there's a casting operator, the cast will fail at runtime if the object is not of that particular type. For instance, I can do this:
List<object> foo = new List<object>();
foo.Add("bar");
foo.Add(1);
foreach(string bar in foo)
{
}
This is legal, but will fail when the iterator reaches the second item, since it is not a string.
You need to have the type of the item - in this case: ListViewItem.
Also, if you're planning to remove items from the collection and are using a foreach loop, you cannot directly remove from the you're looping through - you'd need to add each item to remove to a new collection and remove all items in that collection from the original after the termination of the loop.
Use the beautiful collection caster of LINQ
using System.Linq;
foreach(var item in listView.Items.Cast<ListViewItem>()){
item.BackColor = ...
}

Can I get the item type from a BindingSource?

I would like to get the Type of item that a BindingSource is hooked up to or configured for. The BindingSource.DataSource property can be set to an object, list, or type. If it is a Type, it obviously does not have a bound item yet, but I would still like to get the Type. For a List, I need the item Type, not the list type.
I currently have a custom list type for business objects that implement an IListItemType interface, that I created to solve this problem a while back. I would now like to get this working in a more generic fashion so that it will work with any list.
I've looked through the API docs for for a good way to do this, but so far I have not had any luck. Am I missing something or is this just something I can not or should not be doing?
I recently ran across the ListBindingHelper class in the framework that has everything I was looking for:
System.Windows.Forms.ListBindingHelper.GetListItemType()
Returns the type of an item contained in a list or collection.
ListBindingHelper.GetListItemProperties()
Returns a PropertyDescriptorCollection that describes the properties of the items contained in the list.
There is no completely generic way to get the "type" of the list. The most common method is to examine the first item, but this can be misleading as you can have objects that are of a more specific type in a collection that is less specific (in other words, the collection might be a List<object>, but the first item might be a string, leading you to guess that it's a List<string>). If you're confident that all of the elements will be the same type (meaning none are more specific than the generic type of the collection or than any of the other objects), then examining the first item is the easiest.
Apart from that, you could examine the list's actual type using GetType and check its interfaces. Chances are that any collection that's strongly typed is going to implement IEnumerable<T>, so you can iterate over its interfaces looking for IEnumerable that's generic, then look at its generic type arguments. It's (more than) a little hokey, but it should work.
TL;DR Version
Try this. Assuming you're using .NET 3.5 and have the list stored in a variable called list:
var listType = list.GetType().GetInterfaces()
.Where(t => t.Name == "IEnumerable" && t.IsGenericType)
.Select(t => t.GetGenericArguments()[0]).FirstOrDefault();
As long as the list implements IEnumerable<T>, this will give you T. If it doesn't, chances are the list type is object anyway.
It's been quite a while since this answer has been on board but just in case anybody is still looking for the answer...
I ran into a similar problem. My scenario was that BindingSource.DataSource would always be bound to an IEnumerable BUT there may not be any items in the list. It turns out that BindingSource has a private instance member called "itemType". This field does just what you're looking for: it shows the element type of a list if the BindingSource is bound to a list, or it shows the type of the object that the BindingSource is bound to, if there is no list.
To access the field value, I used some hacky reflection:
FieldInfo fi =
typeof(BindingSource)
.GetField("itemType", BindingFlags.NonPublic | BindingFlags.Instance);
Type myElementType = fi.GetValue(DataBinder.RestrictedDataBinding) as Type;
Without doing much research, I kind of assume that what it's doing is showing the element type of the innerList, which is why it doesn't matter if the DataSource is a list type or not. Also, I assume this field would accurately show the element type of any kind of list that is supported by the BindingSource (including IQueryables, etc.).
WARNING: I have NOT tested this field much so I don't know if there are cases that would make it not read the correct element type. For example, does the field always get accurately updated when the BindingSource's DataSource property is reset? What if the DataSource property is reset to a list that has a different elementType? In my case, these exceptions and others don't apply but you might want to test them.
Lastly, using reflection to hack into private fields breaks all kinds of oop principles. Keep that in mind. Also, keep in mind that there very well might be a good reason why the itemType field was hidden. If you need to investigate further, the code for the BindingSource class is publicly available.

Categories

Resources