10

由于我不确切知道触发错误的具体部分,因此我不完全确定如何更好地标记它。

这个问题是 SO question c# 代码的副产品似乎以无效的方式得到优化,使得对象值变为 null,昨天晚上我试图帮助Gary。他是发现有问题的人,我只是将问题简化为一个更简单的项目,并希望在进一步处理之前进行验证,因此这里有这个问题。

如果其他人可以验证他们也遇到了这个问题,我会在 Microsoft Connect 上发布一条说明,当然我希望 Jon、Mads 或 Eric 也能看看它:)

它涉及:

  • 3个项目,其中2个是类库,其中一个是控制台程序(最后一个不需要重现问题,但是执行这个就可以显示问题,而您需要使用反射器并查看编译后的代码如果你不添加它)
  • 不完整的引用和类型推断
  • 泛型

代码可在此处获得:代码存储库

如果您想亲自动手,我将在下面发布有关如何制作项目的说明。

这个问题通过在方法调用中产生一个无效的转换来表现出来,然后返回一个简单的泛型列表,然后在返回它之前将其转换为奇怪的东西。原始代码以转换为布尔值结束,是的,一个布尔值。编译器在返回结果之前添加了从 aList<SomeEntityObject>到布尔值的强制转换,并且方法签名表示它将返回 a List<SomeEntityObject>。这反过来会导致运行时出现奇怪的问题,从方法调用的结果被认为是“优化掉”(原始问题),或者由于类似异常之一或其中一个而BadImageFormatException导致的崩溃。InvalidProgramException

在我重现这一点的工作中,我看到了void[]转换为 ,我的代码的当前版本现在转换为TypedReference. 在一种情况下,Reflector 崩溃了,因此很可能代码在这种情况下超出了希望。您的里程可能会有所不同。

以下是重现它的方法:

注意:可能有更多的最小形式可以重现该问题,但是将所有代码移至一个项目使其消失。从类中删除泛型也会使问题消失。下面的代码每次都会为我重现该问题,因此我将其保留原样。

我为下面代码中的转义 html 字符道歉,这是 Markdown 在欺骗我,如果有人知道我该如何纠正它,请告诉我,或者只是编辑问题

  1. 为 .NET 4.0 创建包含控制台应用程序的新 Visual Studio 2010 解决方案
  2. 添加两个新项目,都是类库,也是 .NET 4.0(我假设它们被命名为 ClassLibrary1 和 ClassLibrary2)
  3. 调整所有项目以使用完整的 .NET 4.0 运行时,而不仅仅是客户端配置文件
  4. 在控制台项目中添加对 ClassLibrary2 的引用
  5. 将 ClassLibrary2 中的引用添加到 ClassLibrary 1
  6. 删除默认添加到类库中的两个 Class1.cs 文件
  7. 在 ClassLibrary1 中,添加对 System.Runtime.Caching 的引用
  8. 向 ClassLibrary1 添加一个新文件,将其命名为 DummyCache.cs,然后粘贴以下代码:

    using System;
    using System.Collections.Generic;
    using System.Runtime.Caching;
    
    namespace ClassLibrary1
    {
        public class DummyCache<TModel> where TModel : new()
        {
            public void TriggerMethod<T>()
            {
            }
            // Try commenting this out, note that it is never called!
            public void TriggerMethod<T>(T value, CacheItemPolicy policy)
            {
            }
            public CacheItemPolicy GetDefaultCacheItemPolicy()
            {
                return null;
            }
            public CacheItemPolicy GetDefaultCacheItemPolicy(IEnumerable<string> dependentKeys, bool createInsertDependency = false)
            {
                return null;
            }
        }
    }
    
  9. 向 ClassLibrary2 添加一个新文件,将其命名为 Dummy.cs 并粘贴以下代码:

    using System;
    using System.Collections.Generic;
    using ClassLibrary1;
    
    namespace ClassLibrary2
    {
        public class Dummy
        {
            private DummyCache<Dummy> Cache { get; set; }
            public void TryCommentingMeOut()
            {
                Cache.TriggerMethod<Dummy>();
            }
            public List<Dummy> GetDummies()
            {
                var policy = Cache.GetDefaultCacheItemPolicy();
                return new List<Dummy>();
            }
        }
    }
    
  10. 在控制台项目的 Program.cs 中粘贴以下代码:

    using System;
    using System.Collections.Generic;
    using ClassLibrary2;
    
    namespace ConsoleApplication23
    {
        class Program
        {
            static void Main(string[] args)
            {
                Dummy dummy = new Dummy();
                // This will crash with InvalidProgramException
                // or BadImageFormatException, or a similar exception
                List<Dummy> dummies = dummy.GetDummies();
            }
        }
    }
    
  11. 构建,并确保没有编译器错误

  12. 现在尝试运行程序。这应该会因更可怕​​的异常之一而崩溃。我已经看到了 InvalidProgramException 和 BadImageFormatException,这取决于演员最终的结果
  13. 查看Reflector中Dummy.GetDummies的生成代码。源代码如下所示:

    public List<Dummy> GetDummies()
    {
        var policy = Cache.GetDefaultCacheItemPolicy();
        return new List<Dummy>();
    }
    

    然而reflector说(对我来说,它为你选择的演员可能会有所不同,在一种情况下,Reflector甚至崩溃了):

    public List<Dummy> GetDummies()
    {
        List<Dummy> policy = (List<Dummy>)this.Cache.GetDefaultCacheItemPolicy();
        TypedReference CS$1$0000 = (TypedReference) new List<Dummy>();
        return (List<Dummy>) CS$1$0000;
    }
    

现在,这里有一些奇怪的事情,除了上面的崩溃/无效代码:

  • Library2Dummy.GetDummies执行调用以从 Library1 获取类的默认缓存策略。它使用类型推断var policy = ...,结果是一个CacheItemPolicy对象(代码中为 null,但类型很重要)。

    但是,ClassLibrary2 没有对 System.Runtime.Caching 的引用,因此它不应该编译。

    事实上,如果你注释掉 Dummy 中名为 的方法TryCommentingMeOut,你会得到:

    'System.Runtime.Caching.CacheItemPolicy' 类型在未引用的程序集中定义。您必须添加对程序集“System.Runtime.Caching, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”的引用。

    为什么存在这种方法会让编译器高兴我不知道,我什至不知道这是否与当前问题有关。也许这是第二个错误。

  • 里面有一个类似的方法DummyCache,如果你恢复里面的方法Dummy,让代码再次编译,然后注释掉DummyCache上面有“Try commenting this out”注释的方法,你会得到同样的编译错误

4

1 回答 1

2

好的,我下载了您的代码,可以按照描述确认问题。

我没有对此进行任何广泛的修改,但是当我运行和反射器时,Release 构建似乎一切正常(= null ref 异常和干净的反汇编)。
Reflector (6.10.11) 在调试版本上崩溃。

另一个实验:我想知道 CacheItemPolicies 的使用,所以我用我自己的 MyCacheItemPolicy(在第三个类库中)替换它,并且弹出相同的 BadImageFormat 异常。

异常提到:{“错误的二进制签名。(来自 HRESULT 的异常:0x80131192)”}

于 2010-08-01T18:27:51.093 回答