Wai*_*Lee 5 c# visual-studio-2010 compiler-bug visual-studio-2012
BadImageFormatException无论使用.NET版本(2.0、3.0、3.5或4),平台或配置如何,在构造的派生类中重写通用迭代器方法都将导致在使用Visual Studio 2010(VS2010)进行编译时引发该错误。在Visual Studio 2012(VS2012)及更高版本中,该问题无法重现。如何避免这种情况?
当步入in中Main在代码MVCE下方(这通常会执行移动到迭代法),BadImageFormatException当该代码在Visual Studio 2010被编译被抛出:
但不在Visual Studio 2012及更高版本中:
public class Program
{
public static void Main(string[] args)
{
foreach ( var item in new ScrappyDoo().GetIEnumerableItems() )
Console.WriteLine(item.ToString());
}
}
public class ScoobyDoo<T>
where T : new()
{
public virtual IEnumerable<T> GetIEnumerableItems()
{
yield return new T();
}
}
public class ScrappyDoo : ScoobyDoo<object>
{
public override IEnumerable<object> GetIEnumerableItems()
{
foreach ( var item in base.GetIEnumerableItems() )
yield return item;
}
}
Run Code Online (Sandbox Code Playgroud)
使用ILSpy检查代码时,ScrappyDoo.GetIEnumerableItemsVS2010和VS2012二进制文件的编译IL 相同:
.method public hidebysig virtual
instance class [mscorlib]System.Collections.Generic.IEnumerable`1<object> GetIEnumerableItems () cil managed
{
// Method begins at RVA 0x244c
// Code size 21 (0x15)
.maxstack 2
.locals init (
[0] class MysteryMachine.ScrappyDoo/'<GetIEnumerableItems>d__0',
[1] class [mscorlib]System.Collections.Generic.IEnumerable`1<object>
)
IL_0000: ldc.i4.s -2
IL_0002: newobj instance void MysteryMachine.ScrappyDoo/'<GetIEnumerableItems>d__0'::.ctor(int32)
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: ldarg.0
IL_000a: stfld class MysteryMachine.ScrappyDoo MysteryMachine.ScrappyDoo/'<GetIEnumerableItems>d__0'::'<>4__this'
IL_000f: ldloc.0
IL_0010: stloc.1
IL_0011: br.s IL_0013
IL_0013: ldloc.1
IL_0014: ret
} // end of method ScrappyDoo::GetIEnumerableItems
Run Code Online (Sandbox Code Playgroud)同样,Main对于VS2010和VS2012二进制文件,该方法的IL 相同:
.method public hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 69 (0x45)
.maxstack 2
.entrypoint
.locals init (
[0] object item,
[1] class [mscorlib]System.Collections.Generic.IEnumerator`1<object> CS$5$0000,
[2] bool CS$4$0001
)
IL_0000: nop
IL_0001: nop
IL_0002: newobj instance void MysteryMachine.ScrappyDoo::.ctor()
IL_0007: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerable`1<!0> class MysteryMachine.ScoobyDoo`1<object>::get_GetIEnumerableItems()
IL_000c: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<object>::GetEnumerator()
IL_0011: stloc.1
.try
{
IL_0012: br.s IL_0027
// loop start (head: IL_0027)
IL_0014: ldloc.1
IL_0015: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<object>::get_Current()
IL_001a: stloc.0
IL_001b: ldloc.0
IL_001c: callvirt instance string [mscorlib]System.Object::ToString()
IL_0021: call void [mscorlib]System.Console::WriteLine(string)
IL_0026: nop
IL_0027: ldloc.1
IL_0028: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_002d: stloc.2
IL_002e: ldloc.2
IL_002f: brtrue.s IL_0014
// end loop
IL_0031: leave.s IL_0043
} // end .try
finally
{
IL_0033: ldloc.1
IL_0034: ldnull
IL_0035: ceq
IL_0037: stloc.2
IL_0038: ldloc.2
IL_0039: brtrue.s IL_0042
IL_003b: ldloc.1
IL_003c: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0041: nop
IL_0042: endfinally
} // end handler
IL_0043: nop
IL_0044: ret
} // end of method Program::Main
Run Code Online (Sandbox Code Playgroud)在VS2012编译的二进制文件中,有一种方法,<>n__FabricatedMethod4在VS2010中没有出现:
VS2012:
VS2010:
ILSpy无法在VS2010二进制文件中检查IL的“破损”方法,并遇到以下异常:
System.NullReferenceException: Object reference not set to an instance of an object.
at ICSharpCode.Decompiler.Disassembler.DisassemblerHelpers.WriteTo(TypeReference type, ITextOutput writer, ILNameSyntax syntax)
at ICSharpCode.Decompiler.Disassembler.DisassemblerHelpers.WriteTo(TypeReference type, ITextOutput writer, ILNameSyntax syntax)
at ICSharpCode.Decompiler.Disassembler.ReflectionDisassembler.DisassembleMethodInternal(MethodDefinition method)
at ICSharpCode.ILSpy.TextView.DecompilerTextView.DecompileNodes(DecompilationContext context, ITextOutput textOutput)
at ICSharpCode.ILSpy.TextView.DecompilerTextView.<>c__DisplayClass31_0.<DecompileAsync>b__0()
Run Code Online (Sandbox Code Playgroud)
同样,它无法以ScrappyDoo.GetIEnumerableItemsC#形式查看方法的内容,并显示类似的异常:
ICSharpCode.Decompiler.DecompilerException: Error decompiling System.Collections.Generic.IEnumerable`1<System.Object> MysteryMachine.ScrappyDoo::GetIEnumerableItems()
---> System.NullReferenceException: Object reference not set to an instance of an object.
// stack trace elided
Run Code Online (Sandbox Code Playgroud)使用DotPeek检查二进制文件时,VS2010和VS2012编译的代码的反编译代码在foreach语句的表达式上有所不同:
VS2010:
// ISSUE: reference to a compiler-generated method
foreach (object obj in (IEnumerable<object>) this.<>n__FabricatedMethod4())
yield return obj;
Run Code Online (Sandbox Code Playgroud)
VS2012(请注意,反编译的C#与源代码相同,正如预期的那样):
foreach (object obj in base.GetIEnumerableItems())
yield return obj;
Run Code Online (Sandbox Code Playgroud)通过将方法更改为属性,或通过向基数或替代中添加更多逻辑,无法解决问题。
将基本方法更改为return IEnumerable<object>而不是IEnumerable<T>解决该问题(在这种情况下),但这不是可接受的解决方案。
在VS2010中定位.NET 2.0,.NET 3.0,.NET 3.5和.NET 4时,会发生此问题。使用VS2012及更高版本进行编译时,目标框架版本无关紧要,并且代码的行为符合预期。
我知道Visual Studio不会编译代码 -它只是调用MSBuild(或Roslyn),但是在安装了VS2010和VS2012的计算机上,此问题仍然是一个问题:在VS2010中运行代码时,问题仍然存在,何时在VS2012中运行它不是。将生成输出的详细程度设置为“诊断”后,我发现VS2010和VS2012都在使用相同的MSBuild二进制文件
C:\Windows\Microsoft.NET\Framework\v4.0.30319
Run Code Online (Sandbox Code Playgroud)这个问题在VS2015中没有出现(使用Roslyn进行编译)-IL有所不同,但是我想这是可以预期的。
我需要使用Visual Studio 2010,因为在我工作的地方,我们在仅支持2010及更低版本的Windows XP上进行了一些开发。
PEVerify为VS2010编译的代码提供以下输出:
> peverify MysteryMachine2010.exe
Microsoft (R) .NET Framework PE Verifier. Version 4.0.30319.0
Copyright (c) Microsoft Corporation. All rights reserved.
[IL]: Error: [MysteryMachine2010.exe : MysteryMachine.ScrappyDoo::<>n__FabricatedMethod4] [HRESULT 0x8007000B] - An attempt was made to load a program with an incorrect format.
[IL]: Error: [MysteryMachine2010.exe : MysteryMachine.ScrappyDoo+<getIEnumerableItems>d__0::MoveNext] [HRESULT 0x8007000B] - An attempt was made to load a program with an incorrect format.
2 Error(s) Verifying MysteryMachine2010.exe
Run Code Online (Sandbox Code Playgroud)
而对于通过VS2012及更高版本编译的二进制文件,结果如预期的那样:
> peverify "MysteryMachine2012.exe"
Microsoft (R) .NET Framework PE Verifier. Version 4.0.30319.0
Copyright (c) Microsoft Corporation. All rights reserved.
All Classes and Methods in MysteryMachine2012.exe Verified.
Run Code Online (Sandbox Code Playgroud)从命令提示符运行VS2010编译的代码时,将产生以下输出:
> MysteryMachine2010.exe
Unhandled Exception: System.BadImageFormatException: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)
at MysteryMachine.ScrappyDoo.<getIEnumerableItems>d__0.MoveNext()
at MysteryMachine.Program.Main(String[] args) in MysteryMachine\Program.cs:line 11
Run Code Online (Sandbox Code Playgroud)有谁知道这是为什么,以及如何避免它?对于我的实际用例,基础迭代器中没有任何项,因此我制作了基础方法abstract并使所有派生类都覆盖了它,但是这种情况随时可能发生变化,从而使黑客修复程序无用。
| 归档时间: |
|
| 查看次数: |
242 次 |
| 最近记录: |