开发者

.NET Linq to Objects strange behavior

I've got a BadImageFormatException error in this little code below. I know it isn't good practice to write a program in this way but it seems to be a bug in .NET Framework, not in my code.

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>
    {
        pu开发者_C百科blic IEnumerable<int> Test()
        {
            base.Test();
            yield return 0;
        }
    }
}

Any ideas why it doesn't work?


As a side note, you should be declaring B.Test() method as an override, but that's a different issue.

Commenting out the line base.Test(); fixes it. Here's my theory.

The problem is that you are implementing B.Test() using a compiler generated iterator. Part of the process is taking your code and creating a state machine using a private nested class. It would appear that the compiler team did not expect a use case where you would call the base implementation of anything within the iterator.

So in effect your code which is like this:

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

will require an iterator created by the compiler and will translate your lines to make an appropriate state machine. It does not recognize the base call so it must have been copied verbatim. The generated class naturally wouldn't inherit from any other class therefore the base call would fail. Conceptually, the state machine code will have the line somewhere:

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

Though looking at the code generated using Reflector it doesn't actually appear in the assembly (at least, I don't see it).

I wrote a different test case to determine what line it causes a problem:

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

And stepped through the code. It fails on the first MoveNext() call (as I would have thought). Unfortunately I don't know how to step into the generated iterator. So stepping through the disassembled code, it fails on the marked line:

            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 

So maybe the actual problem might be something else but it'll probably be related to this base call.


Here you go. you need to create a delegate method to call the base.Test() method. Also you should use the Test method as override or new.

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

For reference. 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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜