.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
精彩评论