这个问题是c# code seems to get optimized in an invalid way such that an object value becomes null问题的副产物,我试图在昨天晚上帮助Gary.他是一个发现有问题的人,我已经将问题简化为一个更简单的项目,并且在进一步了解之前需要验证,因此这里就是这个问题.
我会在Microsoft Connect上发布一个注释,如果其他人可以验证他们是否也遇到这个问题,当然我希望Jon,Mads或Eric也会看看它:)
它涉及:
> 3个项目,其中2个是类库,其中一个是控制台程序(最后一个不需要重现问题,但只是执行这个显示问题,而您需要使用反射器并查看编译如果你不添加它的代码)
>不完整的引用和类型推断
>泛型
代码可在这里:code repository.
如果您想要让手变脏,我将在下面介绍如何制作项目的说明.
在返回一个简单的通用列表之前,在方法调用中产生一个无效的转换,这个问题就表现出来,在返回之前将其转换成一些奇怪的东西.原始代码最后一个转换为布尔值,是的,一个布尔值.编译器从List< SomeEntityObject>中添加了一个转换在布尔值返回结果之前,方法签名表示它将返回一个List< SomeEntityObject>.这反过来又导致运行时的奇怪问题,从方法调用的结果被认为是“优化掉”(原始问题)的一切,或与BadImageFormatException或InvalidProgramException或类似异常之一的崩溃.
在我的工作中重现这一点,我看到一个演员void [],现在我的代码的版本现在转换为一个TypedReference.在一种情况下,反射器崩溃,因此在这种情况下,代码最有可能超出希望.你的里程可能会有所不同.
以下是如何重现它:
注意:有可能会有更多的最小的形式,将重现的问题,但将所有的代码移动到一个项目,使其消失.从类中删除泛型也使问题消失.下面的代码重现了我每次的问题,所以我要离开它.
对于下面的代码中的转义的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,具体取决于最终的转换
>查看反射器中生成的Dummy.GetDummies代码.源代码如下所示:
public List<Dummy> GetDummies() { var policy = Cache.GetDefaultCacheItemPolicy(); return new List<Dummy>(); }
然而反射器说(对我来说,它可能会在你选择的投射中有所不同,反之亦然)
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的类上的默认缓存策略.它使用类型inference var policy = …,结果是一个CacheItemPolicy对象(代码中为空,但类型很重要).
但是,ClassLibrary2没有对System.Runtime.Caching的引用,所以不应该编译.
事实上,如果您在Dummy中注释了名为TryCommentingMeOut的方法,您将得到:
The type ‘System.Runtime.Caching.CacheItemPolicy’ is defined in an assembly that is not referenced. You must add a reference to assembly ‘System.Runtime.Caching,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a’.
为什么有这样的方法使编译器很开心我不知道,我甚至不知道这是否与当前问题相关联.也许这是第二个bug.
> DummyCache中有一个类似的方法,如果您在Dummy中还原方法,以便代码再次编译,然后注释掉DummyCache中的方法,该方法在其上方有“Try commenting this out”注释,您将得到相同的编译器错误
解决方法
我没有做任何广泛的修补,但是当我跑步反射器一个发布版本似乎都OK(=空参考异常和干净的反汇编).
反射器(6.10.11)在Debug版本上崩溃.
还有一个实验:我想知道使用CacheItemPolicies,所以我用自己的MyCacheItemPolicy(在第3个classlib中)替换它,并且弹出相同的BadImageFormat异常.
异常提到:{“坏二进制签名(来自HRESULT的异常:0x80131192)”}