由于我不知道究竟是哪一部分触发了错误,所以我不完全确定如何更好地标记它。
这个问题是SO问题的副产品C# 代码似乎以无效的方式进行优化,导致对象值变为 null https://stackoverflow.com/questions/3379894/c-code-seems-to-get-optimized-in-an-invalid-way-such-that-an-object-value-become,我试图帮助Gary https://stackoverflow.com/users/301903/gary与昨天晚上。他是那个发现存在问题的人,我刚刚将问题简化为一个更简单的项目,并希望在进一步处理之前进行验证,因此这里有这个问题。
如果其他人可以验证他们也遇到这个问题,我会在 Microsoft Connect 上发布一条说明,当然我希望 Jon、Mads 或 Eric 也能看看它:)
它涉及:
- 3个项目,其中2个是类库,其中一个是控制台程序(不需要最后一个程序来重现问题,但只需执行它就会显示问题,而您需要使用反射器并查看编译后的代码如果你不添加)
- 不完整的引用和类型推断
- Generics
代码可以在这里找到:代码库 http://hg.vkarlsen.no/hgweb.cgi/StackOverflow/file/tip/SO3379894.
如果您想亲自动手,我将在下面发布有关如何制作项目的描述。
该问题的表现是,在返回一个简单的通用列表之前,在方法调用中产生无效的转换,在返回之前将其转换为奇怪的东西。原始代码最终转换为布尔值,是的,布尔值。编译器添加了来自 a 的强制转换List<SomeEntityObject>
在返回结果之前转换为布尔值,并且方法签名表示它将返回一个List<SomeEntityObject>
。这反过来会导致运行时出现奇怪的问题,方法调用的结果中的所有内容都被视为“优化掉”(原始问题),或者崩溃BadImageFormatException
or InvalidProgramException
或类似的例外之一。
在我复制这个的过程中,我看到了一个演员void[]
,我的代码的当前版本现在转换为TypedReference
。在一种情况下,Reflector 崩溃了,因此在这种情况下代码很可能无法实现。您的里程可能会有所不同。
以下是重现它的方法:
Note:可能有更多的最小形式可以重现该问题,但将所有代码移至一个项目即可使其消失。从类中删除泛型也会使问题消失。下面的代码每次都会为我重现该问题,因此我将其保留原样。
我对下面代码中转义的 html 字符表示歉意,这是 Markdown 在捉弄我,如果有人知道我如何纠正它,请告诉我,或者只是编辑问题
- 为 .NET 4.0 创建一个包含控制台应用程序的新 Visual Studio 2010 解决方案
- 添加两个新项目,都是类库,也是 .NET 4.0(我假设它们被命名为 ClassLibrary1 和 ClassLibrary2)
- 调整所有项目以使用完整的 .NET 4.0 运行时,而不仅仅是客户端配置文件
- 在控制台项目中添加对 ClassLibrary2 的引用
- 将类库 2 中的引用添加到类库 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
上面有“尝试注释掉”注释,您会得到相同的编译器错误