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

时间:2009-12-01 15:41:20

标签: c# .net 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类设计很差,因为它实现了2个不同的IEnumerable。这是在我们的产品中“在野外”发现的,所以我真的想知道确切的原因。

3 个答案:

答案 0 :(得分:3)

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

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

当前版本的Enumerable.Cast()首先尝试将IEnumerable转换为IEnumerable&lt;&gt;。这很有效。不使用CastIterator()。

答案 1 :(得分:2)

为什么你认为有一个“正确”的版本可供选择?就个人而言,我很高兴在这一点上抛出模糊的编译器错误。而不是Cast,也许只是常规演员?

var dataSequence = ((IEnumerable<Data>)coll).ToList();

Cast<T>投射数据,而不是变量。

顺便说一句,你在为每个人重新编译吗?我想知道问题是编译器而不是CLR或BCL。

答案 2 :(得分:2)

CLR版本2.0.50727.1433是.NET 2.0 SP1,而CLR版本2.0.50727.4927包括.NET 3.5 SP1(供参考,请参阅Version History of the CLR)。

Enumerable.Cast所述,here的行为已从.NET 3.5更改为.NET 3.5 SP1。

相关问题