What happens when casting IEnumerable to ICollection - c#

Given a case such that:
var collection = myEnumerable as ICollection<MyType>;
What happens under the hood? ICollection has a count property. Does this casting enumerate the enumerable to get the count or does something more involved happen?

Nothing happens. If myEnumerable indeed is an ICollection<MyType> collection will contain it. Otherwise it will be null. Simple as that.

Important point:
When ever you cast IEnumerable type object to ICollectionit will be referenced typed.
Like, removing Item from the collection will remove the item from the IEnumerable collection as well.
For Example:
IEnumerable<AnyClass> enumerableObject;
ICollection<AyClass> collectingObject = (ICollection<AnyClass>)enumerableObject;
foreach(var item in collectingObject){
collectingObject.Remove(item);
}
And if you access value of enumerableObject you will find it updated, with all the value removed.

Related

Item removed from one collection is removed from second collection implicitly too, why?

This is was big confusion for me. I have following code:
IEnumerable<WorkflowStateData> data = model.Data.Except(
request.WorkflowState.CustomData.Select(x => new Model.WorkflowStateData {Key = x.Key}),
new WorkflowStateDataEqualityComparer());
foreach (var item in data) // makes the comparison (and exception) based on the value of property 'Key'
{
model.Data.Remove(item);
stateDataSet.Remove(item);
}
This should create new collection of items and use it for enumeration using foreach. It should remove items from original collection, but it removes it both from original collection and from "data" collection and throws InvalidOperationException: Collection was modified. Why?
Enumerable.Except returns an IEnumerable<T> of the set difference but it does it deferred which means it is still only the query. So everytime you would "touch" data you would execute this query. So if you want a "materialized" collection you have to use ToList, ToArray, ToLookup or ToDictionary (or add the result from a foreach-loop into another collection).
Normally you should even get an exception if you try to modify the collection that you are currently enumerating in a foreach. So you could use ToList to create a real collection which is not afffected if you modify it's source:
List<WorkflowStateData> data = model.Data.Except(
request.WorkflowState.CustomData.Select(x => new Model.WorkflowStateData {Key = x.Key}),
new WorkflowStateDataEqualityComparer())
.ToList();
Now you can remove the object(s) from the original collection without removing it from the list.
You should read about LINQ's deferred execution / eager evaluation.
Answer is pretty simple, but not obvious because of use of that traitorous "var" keyword. Except returns IEnumerable, not ICollection. IEnumerable could be imagined as view to specific collection, it is not collection on its own. So when item was removed from original collection, my enumerable was changed too.

How to iterate over items of a list when I only have access to the object of that list and dont know the type parameter?

Somewhere in my code I have an object that I already know that is a list. But I don't know the type parameter of that list. I need to iterate over it's items. I tried to cast that object to a list of objects but it didn't help me:
List<Object> objList = (List<Object>)(dataModel.Value);
foreach (var item in objList)
{
Console.WriteLine(item.ToString());
}
In the above code, the Value property of dataModel is a list of XYZ values, but it throws an exception when I run this code. It says that, it could not cast XYZ to Object.
Is that possible to do some deserialization and do the job over deserialized objects?
You should cast to IEnumerable<object>, or even just IEnumerable.
A List<string> is not a List<object> as generic variance doesn't apply to classes, and a List<string> is not an IList<object> as IList<T> is not covariant. (It can't be, due to operations which accept a T, such as Add.)
However, IEnumerable<T> is covariant in T which is exactly what you want in this case - but only if your value is a list of reference types; covariance doesn't work with type arguments which are value types... so a List<int> isn't convertible to IEnumerable<object>. It is still convertible to IEnumerable though, so the following code gives you the most flexible solution:
var items = (IEnumerable) dataModel.Value;
foreach (var item in items)
{
Console.WriteLine(item);
}
A List<Something> does not derive from List<object> it derives from object but not from List<object>, this makes sense as it could be very type unsafe. You can always treat a List<int> as an object for example, but if you could treat it as a List<object> you could then call .Add("test") and end up with a List<int> containing a string violating type safety.
However recent .net versions added covariance and contravariance, it doesn't make sense on List<T> but it DOES make sense on IEnumerable<T> (as you can't "add" to an IEnumerable but only enumerate it, it's very fine to enumerate items of arbitrary types as objects).
So you can cast to the interface:
IEnumerable<Object> objList = (IEnumerable<Object>)(dataModel.Value); // This works, List<whatever> is IEnumerable<whatever>, IEnumerable<whatever> can safely be cast to IEnumerable<object>
Or you can cast the individual items themselves if you want to stick to a List by creating a new List as such:
List<object> objlist = dataModel.Value
.Cast<object>() // every item will be cast to object
.ToList(); // then make a new list out of it, this is type safe as the original List<whatever> is not affected.

Add a List<object> or Arraylist to an array of CustomObject[]

I have tried many ways like
Cast<CustomObject>, as Customobject and ToArray(Customobject) but nothing worked.
How can I add List or ArrayList via AddRange to a CustomObject[] Array?
Code is really difficult.
But if you have some time you can get the complete source of the destination list from here:
http://www.codeproject.com/Articles/4012/C-List-View-v1-3?msg=3844172#xx3844172xx
This is a Custom Listview
I activated a combobox for the second column, so I can select diferrent values for a cell.
But before this, I have to add something to select.
This is the hole problem.
Update:
Firstly, thanks for the help !
Secondly, Found a solution in the comments from the website with the source.
Had to add some code and changed the destination custom array to a List
list.Cast<CustomObject>().ToArray()
Will work as long as the things in the list are actually CustomObject. If they might be other types, you can use OfType<CustomObject>() instead of Cast. This will filter out anything of an incompatible type.
Assuming the objects really are instances of CustomObject, use LINQ Select method:
objList.Select(o => o as CustomObject).ToArray();
Otherwise you will get an array of null.
If its a List<CustomObject> then let us say
CustomObject[] coarr = list_of_customobject.ToArray();
If its an ArrayList then
CustomObject[] coarr = arraylist.OfType<CustomObject>().ToArray();
If you are unsure whether all of your objects are of the type CustomObject try
var result = list.OfType<CustomObject>.ToArray();
Strictly speaking you cannot add elements to an array, since an array's length remains constant over its lifetime. There are two things you can do:
Create a new array
myArray = myTList.ToArray() // generic)
myArray = myArrayList.Cast<CustomObject>().ToArray() // cast, non-generic
myArray = myArrayList.OfType<CustomObject>().ToArray() // filter by type, non-generic
Set elements of an array
myArray[x] = myTList[y] // generic
myArray[x] = (CustomObject)myArrayList[y] // non-generic
I recommend you to take the generic collection whenever possible. They provide you additional type safety. Casting object variables cause runtime errors you could detect at compile time by using generic types.
If you actually want to add elements to an existing collection, you may try to use a dynamic collection type rather than an array: List<T> : IList<T> or LinkedList<T> : ICollection<T> are a good point to start, or maybe more specific types like Stack<T> or Queue<T>.

c# collection.RemoveAll(collection.OfType<type>());

Can I do something like this:
collection.RemoveAll(collection.OfType<type>());
To remove all the elements of a given type from a collection?
Both of the already-submitted answers are correct, but omit an explanation why the poster's sample code doesn't work. A couple of interesting points:
First, RemoveAll() is not defined in the ICollection or ICollection<T> interface; it's defined on List<T>, for example, but the semantically equivalent method on HashSet<T> is called RemoveWhere(). If you want to be able to do this for any ICollection<T>, you should write an extension method.
Second, the question's sample code passes a sequence of items to be removed from the collection, but List<T>.RemoveAll() and HashSet<T>.RemoveWhere() take a predicate to identify the items to be removed (as shown in the other answers). You could write your extension method to take the other approach, and pass an IEnumerable<T> as in your example. You need to be careful, though, because you can't do this:
foreach (var item in collection)
if (ShouldRemove(item))
collection.Remove(item);
If you try to do that, you should get an InvalidOperationException with a message like "Collection was modified; enumeration operation may not execute."
As the first two code answers use a List specifically, and the third only has nonworking code:
ICollection collection = GetCollection();
foreach (object obj in collection.Where(obj => obj is type).ToList())
collection.Remove(obj);
You can end the enumeration by calling ToList, so that removal is allowed again. It's totally inefficient tho, as it requires one enumeration to get the objects and then removes them one by one, possibly requiring enumeration each time. If the collection type you're using has it's own methods, like List.RemoveAll, use those instead, but your use of "collection" implies you do not know its type.
Alternatively, if the importance is not on preserving the object, consider reassigning instead:
collection = collection.Where(obj => obj is type == false).ToList();
I would do something like this:
collection.RemoveAll(i => collection.OfType().Contains(i));
EDIT:
collection.RemoveAll(i => i is type);
If I understand it correctly, you have a single collection of any object type and you want to remove all items of a type from that collection. If so, it's simple:
objects.RemoveAll(q=>q.GetType()==typeof(YourType));
collection.Clear() removes all elements.

Retrieve NHibernate.Collections.Generic.PersistentGenericBag as IList<T> using Reflection

I'm attempting to compare a transient object graph to an NHibernate-persisted object graph. Unfortunately my code breaks where properties of type IList<T> are concerned. The code below works fine with instances of List<T>, because List<T> implements both IList<T> AND IList. Unfortunately, NHibernate's PersistentGenericBag only implements IList<T>.
IList list1 = (IList)prop1.GetValue(object1, null);
IList list2 = (IList)prop2.GetValue(object2, null);
If either object1 or object2 is a PersistentGenericBag, I get an error such as:
System.Reflection.TargetInvocationException : Exception has been thrown
by the target of an invocation.
----> System.InvalidCastException : Unable to cast object of type
'NHibernate.Collection.Generic.PersistentGenericBag`1[MyNamespace.MyClass]'
to type 'System.Collections.Generic.List`1[MyNamespace.MyClass]'.
Is there a reliable way to retrieve the PersistentGenericBag instance as an IList<T> using reflection?
I had hoped the popular Compare .NET Objects class would help, but it fails with the exact same error.
Edit: All the answers below are on the right track. The problem was that the getter on the problematic IList<T> property was attempting a cast to List<T>, which obviously can't be done to a PersistentGenericBag. So, my fault for the misguided question.
EDIT: never mind. commenter is right, you CAN go straight to IList. I was focusing one the question as stated a little too hard to see the obvious even as I was coding the answer. doh!
Ok, you just need to dig a little bit deeper.
The base class of PersistentGenericBag is PersistentBag, which does implement IList.
var prop1 = typeof (Customer).GetProperty("Invoice");
// if you need it for something...
var listElementType = prop1.PropertyType.GetGenericArguments()[0];
IList list1;
object obj = prop1.GetValue(object1, null);
if(obj is PersistentBag)
{
list1 = (PersistentBag)obj;
}
else
{
list1 = (IList)obj;
}
foreach (object item in list1)
{
// do whatever you wanted.
}
Tested and works for bags. Take it to the logical conclusion for other list/set/collection types that you might encounter.
So, the short answer is; If you KNOW it is a bag, you can just cast the object first to PersistentBag and then to IList...
IList list = (PersistentBag)obj;
If you DONT KNOW then use some conditional logic as shown.
You don't need an IList to compare two collections.
Cast to IEnumerable instead.

Categories