.NET Linq to Objects 奇怪的行为

标签 .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;
        }
    }
}

有什么想法为什么它不起作用吗?

最佳答案

顺便说一句,您应该将 B.Test() 方法声明为重写,但这是一个不同的问题。

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

问题是您正在使用编译器生成的迭代器实现B.Test()。该过程的一部分是获取代码并使用私有(private)嵌套类创建状态机。编译器团队似乎没有想到您会调用迭代器中任何内容的基本实现的用例。

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

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 

因此,也许实际问题可能是其他问题,但是它可能与此基本调用有关。

关于.NET Linq to Objects 奇怪的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6715640/

相关文章:

c# - 是否有适用于 Windows Phone 7 的免费条形码扫描仪 SDK 可以在我的应用程序中使用?

c# - System.Text.StringBuilder.ToString() 处的 System.OutOfMemoryException 与 Excel XML 字符串

exception - 如何配置 log4j 不打印异常堆栈跟踪?

java - 真的需要自定义异常吗

c# - 如何在 LINQ 中动态创建联接?

c# - 本地化枚举描述属性

c# - 从文件中读取速度不够快,我该如何加快速度?

c# - ASP.NET Core Razor - 全局异常处理程序

c# - 如何优化以下 linq to object 查询

c# - Linq to objects Predicate Builder