2008-10-23 16 views
7

Quelqu'un peut-il me dire pourquoi ce code se comporte comme il le fait? Voir les commentaires intégrés dans le code ...Invoke() et BeginInvoke() se comportent différemment lors de l'exécution d'une méthode remplaçable via un délégué

Est-ce que je manque quelque chose de vraiment évident ici?

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

Je n'ai pas essayé, ou était au courant qu'il y avait un problème, mais je peux voir beaucoup de problèmes à venir de cette situation. Peut-être que quelqu'un peut expliquer :) – leppie

Répondre

5

Je n'ai pas encore de réponse, mais je dois ce que je crois être un programme légèrement plus clair pour démontrer la bizarrerie:

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

Cela ne concerne pas les appels récursifs. Le résultat est toujours le même bizarrerie que:

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

(Si vous êtes d'accord que cela est une simple repro, vous pouvez remplacer le code dans la question initiale et je vais le retirer de ma réponse :)

Pour être honnête, cela ressemble à un bug pour moi. Je vais creuser un peu plus.

+0

Cela ressemble à un bug avec le code interne généré pour BeginInvoke. En regardant la pile du deuxième appel, confirme l'exactitude des informations sur la méthode dans le délégué (toujours MyParent.X). – leppie

+0

Une autre bizarrerie est, pourquoi est-ce que Remoting est utilisé pour un appel asynchrone? Je pensais vraiment qu'il utiliserait simplement un thread ou un threadpool simple. – leppie

+0

Où voyez-vous Remoting arriver? –

0

Peut-être pas la réponse que vous cherchez, mais cela semble fonctionner:

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

ou

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

Mais vous devez faire votre propre comptabilité :(

1

Alors que Delegate.Invoke appelle directement la méthode déléguée, Delegate.BeginInvoke utilise en interne ThreadPool.QueueUserWorkItem(). Md.Invoke() ne pouvait appeler que base.X car les méthodes d'une classe de base sont accessibles wi affiner la classe dérivée via le mot clé de base. Puisque le délégué démarré par le pool de threads est externe à votre classe, la référence à sa méthode X est soumise à une surcharge, tout comme le code ci-dessous.



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

débogage dans le code .NET Framework: http://blogs.msdn.com/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx