Overriding a generic iterator results in a BadImageFormatException error when compiling with Visual Studio 2010

TL; dr:

  • Overriding the general iterator method in the constructed derived class leads to what BadImageFormatExceptionis created when compiling with Visual Studio 2010 (VS2010), regardless of the version of .NET (2.0, 3.0, 3.5, or 4), platform, or configuration. The problem does not reproduce in Visual Studio 2012 (VS2012) and above.
  • The contents of the base method (provided the source is compiled) does not matter, since it does not execute.

How can this be avoided?


Description of the problem

When entering in inin Mainthe MVCE code below (which usually moves execution to the iterator method), a BadImageFormatExceptionis called when the code compiles in Visual Studio 2010:

BadImageFormatException in VS2010

Visual Studio 2012 :

BadImageFormatException in VS2012


MCVE

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;
    }
}

  • ILSpy IL ScrappyDoo.GetIEnumerableItems VS2010 VS2012:

    .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
    
  • , IL Main VS2010 VS2012:

    .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
    
  • , VS2012, <>n__FabricatedMethod4, VS2010:

    VS2012

    FabricatedMethod in Visual Studio 2012

    VS2010

    FabricatedMethod in Visual Studio 2010

    ILSpy IL "" VS2010 :

    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()
    

    , ScrappyDoo.GetIEnumerableItems # :

    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
    
  • DotPeek VS2010- VS2012 foreach:

    VS2010

    // ISSUE: reference to a compiler-generated method
    foreach (object obj in (IEnumerable<object>) this.<>n__FabricatedMethod4())
      yield return obj;
    

    VS2012 ( , # , ):

    foreach (object obj in base.GetIEnumerableItems())
      yield return obj;
    
  • .

  • IEnumerable<object> IEnumerable<T> ( ), .

  • .NET 2.0,.NET 3.0,.NET 3.5 .NET 4 VS2010. VS2012 , .

  • , Visual Studio - MSBuild ( Roslyn), - VS2010 VS2012: VS2010 , VS2012 . Diagnostic , VS2010 VS2012 MSBuild

    C:\Windows\Microsoft.NET\Framework\v4.0.30319
    
  • VS2015 ( Roslyn ) - IL , , .

  • Visual Studio 2010 , , Windows XP, 2010 .

  • 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
    

    , 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.
    
  • 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
    

- , , ? , abstract , , hacka > .

+4
1

, , , VS "" , , , .

, /

public override IEnumerable<object> GetIEnumerableItems()
{
    return getIEnumerableItems();
}

IEnumerable<object> getIEnumerableItems() 
{
    foreach ( var item in base.GetIEnumerableItems() )
        yield return item;
}

,

public override IEnumerable<object> GetIEnumerableItems()
{
    foreach ( var item in baseItems() )
    {
        yield return item;
    }
}

IEnumerable<object> baseItems() 
{
    return base.GetIEnumerableItems();
}

inlining, , ( IL).

,

public override IEnumerable<object> GetIEnumerableItems()
{
    return getIEnumerableItems(base.GetIEnumerableItems());
}

IEnumerable<object> getIEnumerableItems(IEnumerable<object> baseItems) 
{
    foreach ( var item in baseItems )
        yield return item;
}

: , - VS 2010.

+2

Source: https://habr.com/ru/post/1655193/


All Articles