.NET Linq to Objects奇怪的行为

时间:2011-07-16 05:51:59

标签: .net exception linq-to-objects

我在下面的这个小代码中遇到了BadImageFormatException错误。我知道以这种方式编写程序并不是一个好习惯,但它似乎是.NET Framework中的一个错误,而不是我的代码中的错误。

using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var b = new B();
            var bb = b.Test();
            var bbb = bb.Count();
        }
    }

    class A<T>
    {
        public virtual IEnumerable<T> Test()
        {
            yield return default(T);
        }
    }

    class B : A<int>
    {
        public IEnumerable<int> Test()
        {
            base.Test();
            yield return 0;
        }
    }
}

为什么它不起作用的任何想法?

2 个答案:

答案 0 :(得分:3)

作为旁注,您应该声明B.Test()方法作为覆盖,但这是一个不同的问题。

注释掉base.Test();行修复了它。这是我的理论。

问题是您使用编译器生成的迭代器实现B.Test()。部分过程是使用私有嵌套类获取代码并创建状态机。似乎编译器团队不期望在迭代器中调用任何东西的基本实现的用例。

所以实际上你的代码是这样的:

class B : A<int>
{
    public override IEnumerable<int> Test()
    {
        base.Test();
        yield return 0;
    }
}

将需要编译器创建的迭代器,并将转换您的行以生成适当的状态机。它无法识别base调用,因此必须逐字复制。生成的类自然不会从任何其他类继承,因此base调用将失败。从概念上讲,状态机代码将在某处包含该行:

[CompilerGenerated]
private sealed class <Test>d__0 : IEnumerable<T>, IEnumerable, IEnumerator<T>, IEnumerator, IDisposable
{
    bool MoveNext()
    {
        // ...
        base.Test(); // what, base?
        // ...
    }
}

虽然查看使用Reflector生成的代码但它实际上并没有出现在程序集中(至少,我没有看到它)。

我编写了一个不同的测试用例来确定导致问题的行:

System.Console.WriteLine("Starting");
using (var e = bb.GetEnumerator())
{
    System.Console.WriteLine(e.MoveNext());
    System.Console.WriteLine(e.Current);
    System.Console.WriteLine(e.MoveNext());
}

并逐步完成代码。它在第一次MoveNext()调用时失败(正如我所想的那样)。不幸的是我不知道如何进入生成的迭代器。因此,逐步执行反汇编代码,它在标记的行上失败:

            System.Console.WriteLine("Starting");
00000075  mov         ecx,dword ptr ds:[03622088h] 
0000007b  call        63474D1C 
00000080  nop 
            using (var e = bb.GetEnumerator())
00000081  mov         ecx,dword ptr [ebp-44h] 
00000084  call        dword ptr ds:[001E0020h] 
0000008a  mov         dword ptr [ebp-58h],eax 
0000008d  mov         eax,dword ptr [ebp-58h] 
00000090  mov         dword ptr [ebp-48h],eax 
            {
00000093  nop 
                System.Console.WriteLine(e.MoveNext());
00000094  mov         ecx,dword ptr [ebp-48h] 
00000097  call        dword ptr ds:[001E0024h]     // ERROR!!!!!!!!!!!!!!!!
0000009d  mov         dword ptr [ebp-5Ch],eax 
000000a0  mov         ecx,dword ptr [ebp-5Ch] 
000000a3  call        63A48640 
000000a8  nop 
                System.Console.WriteLine(e.Current);
000000a9  mov         ecx,dword ptr [ebp-48h] 
000000ac  call        dword ptr ds:[001E0028h] 
000000b2  mov         dword ptr [ebp-60h],eax 
000000b5  mov         ecx,dword ptr [ebp-60h] 
000000b8  call        63A49388 
000000bd  nop 

所以也许实际问题可能是其他,但它可能与此base电话有关。

答案 1 :(得分:0)

你走了。 您需要创建一个委托方法来调用base.Test()方法。 您还应该使用Test方法作为覆盖或新的。

class A<T>
    {
        public virtual IEnumerable<T> Test()
        {
            yield return default(T);
        }
    }

    class B : A<int>
    {
        public new IEnumerable<int> Test()
        {
            this.MyDelegate();           
            yield return 0;
        }
        private void MyDelegate()
        {
            base.Test();
        }
    }
    public class CompassModel
    {
        public void GetTeamLeadData()
        {
            var b = new B();
            var bb = b.Test();
            var bbb = bb.Count();
        }
    }

供参考。 http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=EN-US&k=k(EHBADIMAGEFORMAT);k(TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERSION%3dV4.0%22);k(DevLang-CSHARP)&rd=true