2009-07-01 3 views
2

Je dois convertir un délégué ouvert (celui dans lequel la cible n'est pas spécifiée) en un délégué fermé efficacement. J'ai profilé mon code, et le coût d'utilisation de CreateDelegate() pour produire un délégué fermé pour une méthode d'instance est une fraction significative (> 60%) du temps d'exécution global (car il a lieu pour chaque nouvelle instance du type).Un moyen d'effectuer la conversion entre les délégués ouverts et fermés

Certaines informations de base sur les délégués ouverts et fermés sont described on the MSDN site dans la documentation de CreateDelegate. Mon approche actuelle, est de trouver un moyen de mettre en cache le délégué ouvert (de sorte que le coût de production n'est encouru qu'une seule fois) et de l'invoquer en utilisant une autre méthode qui fournit le paramètre implicite "this" au délégué.

Un facteur de complication est que je ne connais pas la signature de la méthode que le délégué représentera au moment de la compilation, autre que par un paramètre générique dans le code. En outre, je veux éviter la réflexion (par exemple Invoke() et DynamicInvoke()) car ils ne sont pas meilleurs en termes de performance.

static TDel CreateOpenDelegate<TDel>(MethodInfo mi) 
{ 
    // creates and returns an open delegate for a delegate of signature TDel 
    // that will invoke some method as described by MethodInfo {mi} 
    return (TDel)(object)Delegate.CreateDelegate(typeof(TDel), mi); 
} 

// simplification of some other code... 
// Note that Action<T> is a sample usage, the actual delegate signature and 
// invocation parameters vary, and are defined by the consumers of my code 
private Action<T> myAction = CreateOpenDelegate<Action<U,T>>(someMethodInfo); 
myAction(this, default(T)); // can't do this since {this} is a hidden parameter... 

J'ai déjà lu l'article de Jon Skeet sur Making Reflection Fly and Exploring Delegates, malheureusement, puisque je ne connais pas la signature de délégué à l'avance, je ne vois pas un moyen d'adapter l'approche qui y est décrit.

Toute aide serait appréciée.

Répondre

2

Si je comprends bien vos besoins correctement, vous pourriez probablement utiliser des arbres d'expression pour ce faire - j'ai un poste à explorer le sujet here *. Voici une version simplifiée:

public static D GetMethodAccessor<D>(MethodInfo mi) where D : class 
{ 
    Type[] args = typeof(D).GetGenericArguments(); 
    if (args.Length == 0) 
     throw new ArgumentException("Type argument D must be generic."); 

    Type instanceType = args[0]; 

    // If return type is not null, use one less arg 
    bool isAction = mi.ReturnType == typeof(void); 
    int callArgCount = args.Length - (isAction ? 1 : 2); 
    Type[] argTypes = args.Skip(1).Take(callArgCount).ToArray(); 

    var param = Expression.Parameter(instanceType, "obj"); 
    var arguments = argTypes.Select((t, i) => Expression.Parameter(t, "p" + i)) 
          .ToArray(); 
    var invoke = Expression.Call(param, mi, arguments); 
    var lambda = Expression.Lambda<D>(invoke, 
        Enumerable.Repeat(param, 1).Concat(arguments)); 

    Debug.WriteLine(lambda.Body); 
    return lambda.Compile(); 
} 

Cela dit, je ne sais pas comment la compilation de traitement et d'expression des arguments de type supplémentaire comparerait avec votre méthode.

En ce qui concerne fournir le «ceci» implicite, pourrait utiliser des méthodes d'extension d'utilisation?

private static Action<U,T> myAction = GetMethodAccessor<Action<U,T>>(myMethod); 
public static void MyAction<U,T>(this U u, T t) 
{ 
    myAction(u, t); 
} 

* Notez que le cache dictionnaire basé sur une chaîne que j'utilise dans le poste est terriblement inefficace - vous aurez envie de mettre en cache le délégué à une instance privée comme dans votre exemple.

0

Essayez-vous de convertir un délégué ou un MethodInfo en un délégué fermé?

Si vous essayez de convertir un délégué en un délégué fermé, que diriez-vous de l'exécution, comme ceci?

static Action<T2> Curry<T1, T2>(Action<T1, T2> del, T1 obj) { return p => del(obj, p); } 
+0

Malheureusement, je ne connais pas la signature de la méthode à l'avance, donc je ne peux pas explicitement traiter les paramètres de cette manière. Action <> pourrait être n'importe quel délégué arbitraire défini par un consommateur de mon code. – LBushkin

+0

Si oui, quel type de délégué voulez-vous utiliser? Ou allez-vous avoir deux paramètres génériques> – SLaks