.NET Linq to Objects странное поведение - PullRequest
5 голосов
/ 16 июля 2011

В этом небольшом коде ниже есть ошибка 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 ]

3 голосов
/ 16 июля 2011

В качестве примечания, вы должны объявить 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позвонить.

0 голосов
/ 16 июля 2011

Вот, пожалуйста. вам нужно создать метод делегата для вызова метода base.Test (). Также вы должны использовать метод Test как override или новый.

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

...