2012-10-22 1 views
5

ist da Nehmen wir an, ist eine Methode, die eine variable Anzahl von Argumenten entgegennimmt:Wie ein Delegierter auf ein Ziel mit variablen Anzahl von Argumenten erstellen

void Target(params object[] args); 

dies können wir mit einem konkreten Parameterliste für eine Maßnahme befestigen schafft einen Lambda-Ausdruck:

Action<int, int> someAction += (a, b) => Target(a, b); 

gibt es eine Möglichkeit, diesen Lambda-Ausdruck zu erstellen dynamisch den Handler für jede Art von Veranstaltung befestigen zu können? Etwas wie:

someAction += CreateDelegate(typeof(someAction), Target); 

Ich versuchte Delegate.CreateDelegate zu verwenden, aber es erwartet das Ziel eine Methode mit der konkreten Liste der Argumente zur Verfügung zu stellen. Ich habe das Gefühl, dass es mit Expression.Lambda möglich sein sollte, aber jetzt hatte ich keinen Erfolg. Haben sie eine Idee?

bearbeiten

Umbenannt Ereignis Aktion und Handler zu zielen.

+1

Ereignisse sollte natürlich genau 2 Parameter : Absender und Argumente. –

+0

@Henk Holterman INHO-Ereignisse können auch Aktionen sein. Es ist möglich, Argumente direkt zu übergeben, und Sie müssen sie nicht in ein EventArgs-Objekt packen. Und wie oft verwenden Sie das Senderobjekt in einem Handler? –

Antwort

3

nahm ich einen Blick auf die Ausdrücke hinter dem Lambda durch folgende Zeile Analyse:

Expression<Action<int, int>> ex = (a, b) => Target(a, b); 

Auf dieser Grundlage ich eine eigene Delegierte Fabrik erstellt:

public static Delegate CreateDelegate(Type delegateType, Action<object[]> target) 
{ 
    var sourceParameters = delegateType.GetMethod("Invoke").GetParameters(); 

    var parameters = sourceParameters.Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray(); 

    var castParameters = parameters.Select(p => Expression.TypeAs(p, typeof(object))).ToArray(); 

    var createArray = Expression.NewArrayInit(typeof(object), castParameters); 

    var invokeTarget = Expression.Invoke(Expression.Constant(target), createArray); 

    var lambdaExpression = Expression.Lambda(delegateType, invokeTarget, parameters); 

    return lambdaExpression.Compile(); 
} 
+1

Das ist großartig! Vielen Dank. –

5

Ein Delegierter für diese Methode:

void Handle(params object[] args); 

Action<object[]> sein würde, als die Delegierten nicht die params Modifikator verwenden können. Sie müssten tun, was der Compiler tut, und die andere Methode einem Objekt-Array zuordnen.

Das Schlüsselwort params wird vom Compiler verarbeitet, sodass die Laufzeit die Methode so verwendet, als ob sie nur ein normales Objektarray benötigt. Um dies zu tun, müssten Sie ein Objekt-Array der entsprechenden Liste erstellen, es mit Ihren Objekten füllen und dann die Methode, die das tut, an Ihren Handler anhängen.

+3

Nicht ganz - 'Aktion ' hat nicht den 'params' Modifizierer, also kann man es nicht als 'action (foo, bar, baz)' bezeichnen, wohingegen ein Delegat, der * wirklich * diese Signatur * hatte * sein würde so aufrufbar. –

+0

Danke für Ihre schnelle Antwort! Es besteht immer noch das Problem, 'Action ' auf eine andere 'Aktion <-etwas->' abzubilden. Ich muss das dynamisch machen. Vielleicht über Reflektion? –

+0

@JonSkeet Ich bearbeitet, um expliziter zu sein, aber das war irgendwie mein Punkt - Delegaten können nicht den Modifikator 'params' verwenden, also müsste der OP den Methodenaufruf in ein Objektarray wie innerhalb seiner aufrufenden Methode einschließen. –

Verwandte Themen