c# – 相同的代码在不同的机器上的行为不同 – 可能是什么原因? (CLR版本问题?)

我刚刚完成调试问题,我们的程序在生产服务器上崩溃,但从未在开发机器上崩溃.

我制作了这个小程序,我可以用以下方法重现这个问题:

using System;
using System.Collections.Generic;
using System.Linq;

namespace RunTimeBug
{
    class Program
    {
        static void Main(string[] args)
        {
            var coll = new Collection {{"Test", new Data()}, {"Test2", new Data()}};

            var dataSequence = coll.Cast<Data>().ToList();
            Console.WriteLine(dataSequence.Count);
        }
    }

    class Collection : Dictionary<string,Data>, IEnumerable<Data>
    {
        public new IEnumerator<Data> GetEnumerator()
        {
            foreach(var v in Values)
                yield return v;
        }
    }

    class Data { }
}

在我的机器上运行时,此代码打印“2”.在生产服务器上运行时,它失败并出现以下异常:

Unhandled Exception: System.InvalidCastException: Unable to cast object of type 'System.Collections.Generic.KeyValuePair
`2[System.String,RunTimeBug.Data]' to type 'RunTimeBug.Data'.
   at System.Linq.Enumerable.<CastIterator>d__b0`1.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at RunTimeBug.Program.Main(String[] args)

我可以在这些机器上找到的唯一区别是,CLR Runtime在机器上的版本为2.0.50727.4927,在它可以工作的机器上,以及版本2.0.50727.1433在它不起作用的机器上.

据我所知,Cast扩展方法在旧机器上获取错误版本的IEnumerable,但在新机器上获得“正确”版本.

任何人都可以解释为什么我看到这个? 2个CLR运行时版本之间发生了哪些变化,可能导致这种情况?

请注意,我已经部署了一个修复程序,我知道上面代码中的Collection类设计很差,因为它实现了两个不同的IEnumerable.这是在我们的产品中“在野外”发现的,所以我真的想知道确切的原因.

解决方法:

我认为Jason链接到的文章中提到的修复与您在以后的版本中工作的代码有关.我无法访问Enumerable.Cast的SP1之前的版本,因此有点难以猜测.

有一件事是肯定的:一旦代码进入Enumerable.CastIterator(),那么保证不会在你的情况下工作.这设置了一个迭代器,将IEnumerable转换为IEnumerable<>并且迭代器调用GetEnumerator()来初始化迭代器块.那只能调用Dictionary.GetEnumerator(),而不是你的.如此异常告诉您,将此IEnumerable返回到Data的KeyValuePair转换为当然会失败.

当前版本的Enumerable.Cast()首先尝试将IEnumerable转换为IEnumerable<>.这很有效.不使用CastIterator().

上一篇:Learning hard 学习笔记


下一篇:c# – 为什么Int32.ToString()发出调用指令而不是callvirt?