好的,这有点奇怪。忽略我想做的事情,看看在这种情况下会发生什么的结果。
守则:
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
}
}
情况:
行numbers = rawNumbers.Split(',').Cast<int>();
出现以便工作,并且不会抛出任何异常。但是,当我遍历集合时,抛出InvalidCastException。
现在,深入了解源代码并查看CastIterator<TResult>
。这似乎是在for (int i = 0; i < numbers.Count(); i++)
...专门numbers.Count()
static IEnumerable<TResult> CastIterator<TResult>(IEnumerable source) {
foreach (object obj in source) yield return (TResult)obj;
}
错误发生在演员表上,当我查看source
变量中的数据时,它是一个字符串[]。
我原本以为,因为我将成功调用强制转换成int的行所以一切都很好。幕后发生了什么?字符串数组真的只是存储在某个地方,并且在被调用之前没有被转换为T吗?懒惰的铸造也许?
我知道我不能这样做:(int)“42”。我的问题不是如何使演员工作,而是发生了什么。演员的延期执行?我称之为Cast<int>()
的行似乎很奇怪,但实际上并没有。
答案 0 :(得分:24)
有几件事:第一,你不能施放 string
到int
。您可以解析字符串并获得结果整数。但基本上你是在尝试这样做:
int x = (int)"32";
这是一个例外。 其次,LINQ运算符是延迟执行的,所以在你尝试迭代结果之前没有实际发生,那就是真正的工作开始时。
使用延迟执行时,不会检查您请求的操作是有效还是可以正确执行,它只是设置操作,然后尝试在迭代时单独执行。
答案 1 :(得分:8)
正如已经指出的那样,'延期执行'是这里的问题。在迭代结果变量之前,许多LINQ运算符不会导致lambda代码实际执行。
这样做的一个原因是允许您构建一系列可以一次执行的复杂操作,而不是作为一系列单独的操作。这可能是一件有用的事情 - 但如果你不期待它也会很棘手。
答案 2 :(得分:5)
Cast
似乎有用的原因是因为调用方法时不会枚举IEnumerable
,而是在调用Count()
时。所以,对Cast()
的调用确实没有做任何事情。一旦实际评估了Cast,代码就会失败。
而不是尝试施放,只需进行简单的转换。
numbers = rawNumbers.Split(',').Select(str => int.parse(str));
答案 3 :(得分:5)
您在Cast<int>()
行没有立即收到错误的原因是因为记住这只是链接操作序列。只有在遍历集合后,才会执行转换。
rawNumbers.Split(',')
马上发生,但Cast<int>()
是延迟计算。
如果在该行上添加了ToList()或ToArray(),它将立即执行。
答案 4 :(得分:-1)
我可能错了,但我想知道它是否与int和string之间的转换有关。当我忘记你不能使用(int)来对抗字符串时,我总是会遇到错误,而你必须使用int.Parse。它可能与此有关,但这只是我的头脑。