2008-10-23 9 views
7

¿Alguien puede decirme por qué este código se comporta de la manera en que lo hace? Ver comentarios incrustados en el código ...Invoke() y BeginInvoke() comportándose de manera diferente al ejecutar un método sobrescribible a través de un delegado

¿Me falta algo realmente obvio aquí?

using System; 
namespace ConsoleApplication3 
{ 
    public class Program 
    { 
     static void Main(string[] args) 
     { 
      var c = new MyChild(); 
      c.X(); 
      Console.ReadLine(); 
     } 
    } 

    public class MyParent 
    { 
     public virtual void X() 
     { 
      Console.WriteLine("Executing MyParent"); 
     } 
    } 

    delegate void MyDelegate(); 

    public class MyChild : MyParent 
    { 
     public override void X() 
     { 
      Console.WriteLine("Executing MyChild"); 
      MyDelegate md = base.X; 

      // The following two calls look like they should behave the same, 
      // but they behave differently!  

      // Why does Invoke() call the base class as expected here... 
      md.Invoke(); 

      // ... and yet BeginInvoke() performs a recursive call within 
      // this child class and not call the base class? 
      md.BeginInvoke(CallBack, null); 
     } 

     public void CallBack(IAsyncResult iAsyncResult) 
     { 
      return; 
     } 
    } 
} 
+0

no he probado esto, o fue consciente de que había un problema, pero puedo ver una gran cantidad de problemas que vienen de esto. Tal vez alguien pueda explicar :) – leppie

Respuesta

5

no tengo una respuesta todavía, pero tengo lo que yo creo que es un programa ligeramente más claro para demostrar la rareza:

using System; 

delegate void MyDelegate(); 

public class Program 
{ 
    static void Main(string[] args) 
    { 
     var c = new MyChild(); 
     c.DisplayOddity(); 
     Console.ReadLine(); 
    } 
} 

public class MyParent 
{ 
    public virtual void X() 
    { 
     Console.WriteLine("Executing MyParent.X"); 
    } 
} 

public class MyChild : MyParent 
{ 
    public void DisplayOddity() 
    { 
     MyDelegate md = base.X; 

     Console.WriteLine("Calling Invoke()"); 
     md.Invoke();    // Executes base method... fair enough 

     Console.WriteLine("Calling BeginInvoke()"); 
     md.BeginInvoke(null, null); // Executes overridden method! 
    } 

    public override void X() 
    { 
     Console.WriteLine("Executing MyChild.X"); 
    } 
} 

Esto no implica ninguna llamada recursiva. El resultado sigue siendo el mismo aunque rareza:

Calling Invoke() 
Executing MyParent.X 
Calling BeginInvoke() 
Executing MyChild.X 

(Si está de acuerdo que este es un repro más simple, no dude en reemplazar el código en la pregunta original y lo retiraré de mi respuesta :)

Para ser sincero, esto parece un error para mí. Excavaré un poco más.

+0

Parece un error con el código interno generado para BeginInvoke. Al observar la pila de la segunda llamada, confirma la "corrección" de la información del método en el delegado (aún MyParent.X). – leppie

+0

Otra rareza es, ¿por qué se usa Remoting para una llamada asíncrona? Realmente pensé que solo usaría un hilo simple o un grupo de hilos. – leppie

+0

¿Dónde ves que entra Remoting? –

0

Quizás no sea la respuesta que está buscando, pero esto parece funcionar:

ThreadPool.QueueUserWorkItem(x => md()); 

o

new Thread(() => md()).Start(); 

Pero tendrá que hacer su propia contabilidad :(

1

Mientras Delegate.Invoke llama directamente al método de delegado, Delegate.BeginInvoke usa internamente ThreadPool.QueueUserWorkItem(). Md.Invoke() solo puede llamar a base.X porque los métodos de una clase base son accesibles con adelgazar la clase derivada a través de la palabra clave base. Como el delegado iniciado por el grupo de subprocesos es externo a su clase, la referencia a su método X está sujeta a sobrecarga, al igual que el código siguiente.



    public class Program 
    { 
     static void Main(string[] args) 
     { 
      MyChild a = new MyChild(); 
      MyDelegate ma = new MyDelegate(a.X); 

      MyParent b = new MyChild(); 
      MyDelegate mb = new MyDelegate(b.X); 

      ma.Invoke(); 
      mb.Invoke(); 
      ma.BeginInvoke(CallBack, null); 
      mb.BeginInvoke(CallBack, null); //all four calls call derived MyChild.X 

      Console.ReadLine(); 
     } 

     public static void CallBack(IAsyncResult iAsyncResult) 
     { 
      return; 
     } 
    } 

depuración en código .NET Framework: http://blogs.msdn.com/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx

Cuestiones relacionadas