2008-11-06 11 views
4

J'ai une classe appelée EventConsumer qui définit un événement EventConsumed et une méthode OnEventConsumed comme suit:Quel est le MSIL pour appeler le gestionnaire d'événement d'une classe de base?

public event EventHandler EventConsumed; 

public virtual void OnEventConsumed(object sender, EventArgs e) 
{ 
    if (EventConsumed != null) 
     EventConsumed(this, e); 
} 

je besoin d'ajouter des attributs au moment de l'exécution OnEventConsumed, donc je suis générer une sous-classe en utilisant System.Reflection. Émettre. Ce que je veux est l'équivalent MSIL de ceci:

public override void OnEventConsumed(object sender, EventArgs e) 
{ 
    base.OnEventConsumed(sender, e); 
} 

Ce que j'est jusqu'à présent ceci:

... 

MethodInfo baseMethod = typeof(EventConsumer).GetMethod("OnEventConsumed"); 
MethodBuilder methodBuilder = typeBuilder.DefineMethod("OnEventConsumed", 
                 baseMethod.Attributes, 
                 baseMethod.CallingConvention, 
                 typeof(void), 
                 new Type[] {typeof(object), 
                    typeof(EventArgs)}); 

ILGenerator ilGenerator = methodBuilder.GetILGenerator(); 

// load the first two args onto the stack 
ilGenerator.Emit(OpCodes.Ldarg_1); 
ilGenerator.Emit(OpCodes.Ldarg_2); 

// call the base method 
ilGenerator.EmitCall(OpCodes.Callvirt, baseMethod, new Type[0]); 

// return 
ilGenerator.Emit(OpCodes.Ret); 

... 

Je crée le type, créez une instance du type, et appeler sa fonction OnEventConsumed, et j'obtiens:

Common Language Runtime detected an invalid program. 

... ce qui n'est pas vraiment utile. Qu'est-ce que je fais mal? Quel est le MSIL correct pour appeler le gestionnaire d'événements de la classe de base?

Répondre

6

Ici « S l'IL d'un exemple d'application:


.method public hidebysig virtual instance void OnEventConsumed(object sender, class [mscorlib]System.EventArgs e) cil managed 
    { 
     .maxstack 8 
     L_0000: nop 
     L_0001: ldarg.0 
     L_0002: ldarg.1 
     L_0003: ldarg.2 
     L_0004: call instance void SubclassSpike.BaseClass::OnEventConsumed(object, class [mscorlib]System.EventArgs) 
     L_0009: nop 
     L_000a: ret 
    } 

Je pense que vous ne chargez pas l'instance parce que vous ne faites pas un ldarg.0

0

L'utilisation de

public virtual void OnEventConsumed(object sender, EventArgs e) 
{ 
    if (EventConsumed != null) 
     EventConsumed(this, e); 
} 

devrait être

public virtual void OnEventConsumed(EventArgs e) 
{ 
    EventHandler handler = this.EventConsumed; 
    if (null != handler) handler(this, e); 
} 

.

Je pense, vous devez utiliser un ILGenerator.EmitCalli et vous devriez passer un type de valeur de retour (je pense que nul dans ce cas) et passer les types d'arguments - je pense « nouveau type [] {typeof (EventArgs)}

+0

pourquoi la deuxième forme? La première fonctionne aussi bien. – Simon

+0

C'est la cause du multithreading. Si vous cochez "EventConsumed! = Null", un autre thread peut être activé, supprimer le délégué de l'événement, le premier thread sera activé, le "EventConsumed" sera null, et le "EventConsumed (this, e)" sera Échec sur NullReferenceException. – TcKs

1

J'était en fait très proche - le problème était que je ne chargeait pas le « ce » argument et que callvirt appelle la méthode de la sous-classe, où je fait voulais appeler ainsi cette section devient:.

// load 'this' and the first two args onto the stack 
ilGenerator.Emit(OpCodes.Ldarg_0); 
ilGenerator.Emit(OpCodes.Ldarg_1); 
ilGenerator.Emit(OpCodes.Ldarg_2); 

// call the base method 
ilGenerator.EmitCall(OpCodes.Call, baseMethod, new Type[0]); 

// return 
ilGenerator.Emit(OpCodes.Ret); 

Maintenant cela fonctionne bien