2011-07-16 24 views
5

Tengo un error BadImageFormatException en este pequeño código a continuación. Sé que no es una buena práctica escribir un programa de esta manera, pero parece ser un error en .NET Framework, no en mi código..NET Linq to Objects comportamiento extraño

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

¿Alguna idea de por qué no funciona?

+0

Ese es un problema muy poco probable de obtener solo de lógica, y es muy poco probable que sea un error .NET. ¿Qué cambios tiene que hacer para que el código se ejecute (en otras palabras, qué cambio específico desencadena el problema)? –

+0

Aunque puedo reproducir el problema. Interesante ... –

+0

¿Dónde está Skeet o Lippert? –

Respuesta

3

Como nota al margen, debe declarar el método B.Test() como una anulación, pero ese es un problema diferente.

Comentando la línea base.Test(); lo corrige. Aquí está mi teoría

El problema es que está implementando B.Test() utilizando un iterador generado por el compilador. Parte del proceso es tomar su código y crear una máquina de estado usando una clase privada anidada. Parece que el equipo del compilador no esperaba un caso de uso en el que llamaras a la implementación base de cualquier cosa dentro del iterador.

Así pues, en efecto, que su código es así:

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

requerirá un iterador creado por el compilador y se traducirá sus líneas para hacer una máquina de estado apropiado. No reconoce la llamada base, por lo que debe haber sido copiada textualmente. La clase generada, naturalmente, no heredaría de ninguna otra clase, por lo que la llamada base fallaría. Conceptualmente, el código máquina de estado tendrá la línea en alguna parte:

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

Aunque mirando el código generado usando Reflector no aparece realmente en la asamblea (por lo menos, yo no lo veo).

escribí un caso de prueba diferentes para determinar cuál es la línea que causa un problema:

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

y dio un paso a través del código. Falla en la primera llamada MoveNext() (como hubiera pensado). Lamentablemente, no sé cómo ingresar al iterador generado. Así que paso a paso por el código desensamblado, falla en la línea marcada:

  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 

Así que tal vez el problema real podría ser otra cosa pero que probablemente va a estar relacionado con este base llamada.

0

Aquí tienes. necesita crear un método de delegado para llamar al método base.Test(). También debe usar el método de prueba como sobrescribir o nuevo.

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

Como referencia. 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