Did I really just put a string data type into an IEnumerable<int>
Ok, this is a little weird. Ignore what I am trying to do, and look at the result of what happens in this situation.
The Code:
static string rawNumbers="1,4,6,20,21,22,30,34";
static IEnumerable<int> numbers = null;
static void Main(string[] args)
{
numbers = rawNumbers.Split(',').Cast<int>();
for (int i = 0; i < numbers.Count(); i++)
{
//do something
}
}
The Situation:
The line numbers = rawNumbers.Split(',').Cast<int>();
appears to work, and no exception is thrown. However, when I iterate over the collection, and InvalidCastException is thrown.
Now, drill down into the source and look at CastIterator<TResult>
. This seems to be getting called at for (int i = 0; i < numbers.Count(); i++)
...specifically numbers.Count()
static IEnumerable<TResult> CastIterator<TResult>(IEnumerable source) {
foreach (object obj in source) yield return (TResult)obj;
}
The error is happening on the cast, and when I look at the data in the source
varaible it is a string[].
I would have thought that since the line where I call the cast to an int executed successfully everything was fine. What is going on behind the curtain? Is the string array really just being stored somewhere, and not being casted to T until it is called for? Lazy casting perhaps?
I know I cant do: (int)"42". My questions isn't how to make the cast work, but what is going on. Deferred execution of the cast? It seems weird the line where开发者_运维问答 I call out Cast<int>()
seems to work, but really doesn't.
A couple of things: first, you can't cast a string
to an int
. You can parse a string and get a resulting integer. But essentially you're trying to do this:
int x = (int)"32";
That's an exception. Second, the LINQ operators are deferred execution, so nothing actually happens until you attempt to iterate the result, that's when the real work starts happening.
With deferred execution there is no checking on whether the operations you're requesting are valid or can be properly performed, it just sets up the operations and then attempts to perform then individually upon iteration.
As has been noted, 'Deferred Execution' is the issue here. Many LINQ operators do not cause your lambda code to actually execute until you iterate over the result variable.
One reason for this is to allow you to build up a complex series of operations that can be executed at one time, rather than as a string of separate operations. This can be a useful thing - but it also can be tricky if you aren't expecting it.
The reason you are not getting an error immediately at the Cast<int>()
line is because remember this is just chaining up the sequence of operations. Only once you iterate over the collection will the conversions be executed.
rawNumbers.Split(',')
happens right away, but the Cast<int>()
is deferred computation.
If you added a ToList() or ToArray() on that line, it would have executed immediately.
The reason that Cast
appears to work, is because the IEnumerable
isn't enumerated when the method is called, but rather when you call Count()
. So, the call to Cast()
really does nothing. The code fails as soon as the Cast is actually evaluated.
Instead of trying to cast, just do a simple conversion.
numbers = rawNumbers.Split(',').Select(str => int.parse(str));
I may be wrong but I wonder if it has to do with casting between int and string. I always get errors when I forget you cannot cast against string using (int), rather you have to use int.Parse. It probably has something to do with that but this is just off the top of my head.
精彩评论