Cannot add items to an IList/List being casted from an IEnumerable
I'm trying to create a small proof-of-concept application for my boss, however the code I've created that simulates what he's trying to pull off isn't working.
for (int i = 0; i < 5000; i++)
{
((IList<string>) obj2.Stuff).Add("Iteration " + i.ToString());
}
开发者_C百科I'm trying to pull this off all in one line because this is what his code looked like the other day in the framework we're working on. Anywho when the code above executes, I get a runtime error saying "Collection was of a fixed-size". And when I try casting to a List instead of an IList, I get an InvalidCastException saying "Unable to cast object of type 'System.String[]' to type 'System.Collections.Generic.List`1[System.String]'."
Anybody have any ideas on how I can pull off a single-line cast to add an item to the IEnumerable or help me figure out a way around the two errors I keep getting? Thanks in advance.
EDIT (4/19/2011 10:49AM EST) I'm adding more code to help people out -- probably should've done this earlier. Sorry.
Program.cs:
#region Cast Test
Class1 obj2 = new Class1();
obj2.Stuff = Enumerable.Empty<string>();
Console.WriteLine("Cast - Start Time: " + 0 + "ms");
Stopwatch stopwatch2 = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 5000; i++)
{
((IList<string>) obj2.Stuff).Add("Iteration " + i.ToString());
}
stopwatch2.Stop();
Console.WriteLine("Cast - Stop Time: " + stopwatch2.ElapsedMilliseconds.ToString() + "ms");
#endregion
Class1.cs:
public class Class1
{
private IEnumerable<string> stuff;
public IEnumerable<string> Stuff
{
get { return stuff; }
set { stuff = value; }
}
}
Arrays in C# are of fixed size. You can't add items to them.
You cannot cast a string[]
to IList<string>
because string[]
does not implement that interface. You will need to create an object implementing IList<string>
first:
List<string> list = obj2.Stuff.ToList();
for (int i = 0; i < 5000; i++)
{
list.Add("Iteration " + i.ToString());
}
Why cast inside the for loop? While every IList is an IEnumerable, not every IEnumerable is an IList. You can use the ToList extention method to copy the IEnumerable to a new List. (Just be sure to be using System.Linq;
)
Also, in this example, there is no need for a for loop:
var list = obj2.Stuff.ToList();
new list.AddRange(Enumerable.Range(0, 5000).Select(i => "Iteration " + i.ToString()));
In comments on one of the answers above you say "I'm trying to avoid copying the list."
You are trying to add data to an object whose underlying type is IEnumerable<T>
. But an IEnumerable
object is not an actual container, it's an interface. You can't add stuff to an interface. But you can assign another object that implements that interface to it.
So to use foson's example above, you could just do:
obj2.Stuff = Enumerable.Range(0, 5000).Select(i => "Iteration " + i.ToString());
Note that when this code executes, nothing actually happens. No objects will be created until something actually iterates over obj2.Stuff
. When that happens, the methods in LINQ will be called that create objects one at a time and return them to the iterator loop.
But there's no actual storage device involved here. You can iterate over obj2.Stuff
and unless you consequently added each integer to something else, they would be gone at the next iteration.
The fundamental point here is you can't store things in IEnumerable
, rather, IEnumerable
is a way to return objects in sequence, and that could be from a list construct, or from a function that generates a sequence.
You can't call Add
since obj2.Stuff
seems to be an array, and an array has a fixed size, as the error message indicates.
The type System.String[]
is an array of strings, which implements IEnumerable<string>
but is not a List<string>
. Arrays are of fixed length. Why do you need it in one line?
Change the type of obj2.Stuff to something like StringCollection or List . In .NET arrays are of fixed length and you can't add new objects to them, so use collections instead.
An IEnumerable
does not give you the same features that an IList
. Unless your Stuff
is an IEnumerable
that is also an IList
your code won't work.
The contract of the first only promises that you can iterate it one element at a time. It never states that you can add new elements.
You can only pull this off if and only if at runtime your enumerable object also satisfies the IList
interface.
As obj2.Stuff
is a string array, it's not possible to do what you are trying to do. An array can't be resized, so you can't add items to it.
If you can replace the array in obj2.Stuff
with another array, you can get the data from the array and make a list from it, add items to the list, get the data from the list and create an array from it, and use that to replace the original array:
List<string> items = new List<string>(obj2.Stuff);
for (int i = 0; i < 5000; i++) {
items.Add("Iteration " + i.ToString());
}
obj2.Stuff = items.ToArray();
If the obj2.Stuff
property is read-only, then you can't add anything to it unless you change then underlying implementation from an array to a collection that you can add items to.
精彩评论