由于我不确切知道触发错误的具体部分,因此我不完全确定如何更好地标记它。
这个问题是 SO question c# 代码的副产品似乎以无效的方式得到优化,使得对象值变为 null,昨天晚上我试图帮助Gary。他是发现有问题的人,我只是将问题简化为一个更简单的项目,并希望在进一步处理之前进行验证,因此这里有这个问题。
如果其他人可以验证他们也遇到了这个问题,我会在 Microsoft Connect 上发布一条说明,当然我希望 Jon、Mads 或 Eric 也能看看它:)
它涉及:
- 3个项目,其中2个是类库,其中一个是控制台程序(最后一个不需要重现问题,但是执行这个就可以显示问题,而您需要使用反射器并查看编译后的代码如果你不添加它)
- 不完整的引用和类型推断
- 泛型
代码可在此处获得:代码存储库。
如果您想亲自动手,我将在下面发布有关如何制作项目的说明。
这个问题通过在方法调用中产生一个无效的转换来表现出来,然后返回一个简单的泛型列表,然后在返回它之前将其转换为奇怪的东西。原始代码以转换为布尔值结束,是的,一个布尔值。编译器在返回结果之前添加了从 aList<SomeEntityObject>
到布尔值的强制转换,并且方法签名表示它将返回 a List<SomeEntityObject>
。这反过来会导致运行时出现奇怪的问题,从方法调用的结果被认为是“优化掉”(原始问题),或者由于类似异常之一或其中一个而BadImageFormatException
导致的崩溃。InvalidProgramException
在我重现这一点的工作中,我看到了void[]
转换为 ,我的代码的当前版本现在转换为TypedReference
. 在一种情况下,Reflector 崩溃了,因此很可能代码在这种情况下超出了希望。您的里程可能会有所不同。
以下是重现它的方法:
注意:可能有更多的最小形式可以重现该问题,但是将所有代码移至一个项目使其消失。从类中删除泛型也会使问题消失。下面的代码每次都会为我重现该问题,因此我将其保留原样。
我为下面代码中的转义 html 字符道歉,这是 Markdown 在欺骗我,如果有人知道我该如何纠正它,请告诉我,或者只是编辑问题
- 为 .NET 4.0 创建包含控制台应用程序的新 Visual Studio 2010 解决方案
- 添加两个新项目,都是类库,也是 .NET 4.0(我假设它们被命名为 ClassLibrary1 和 ClassLibrary2)
- 调整所有项目以使用完整的 .NET 4.0 运行时,而不仅仅是客户端配置文件
- 在控制台项目中添加对 ClassLibrary2 的引用
- 将 ClassLibrary2 中的引用添加到 ClassLibrary 1
- 删除默认添加到类库中的两个 Class1.cs 文件
- 在 ClassLibrary1 中,添加对 System.Runtime.Caching 的引用
向 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; } } }
向 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>(); } } }
在控制台项目的 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(); } } }
构建,并确保没有编译器错误
- 现在尝试运行程序。这应该会因更可怕的异常之一而崩溃。我已经看到了 InvalidProgramException 和 BadImageFormatException,这取决于演员最终的结果
查看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; }
现在,这里有一些奇怪的事情,除了上面的崩溃/无效代码:
Library2
Dummy.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”注释的方法,你会得到同样的编译错误