Comment créer un délégué ouvert à partir d’une méthode d’instance de struct?

J’ai une structure avec une méthode privée que j’aimerais invoquer. Comme je prévois de le faire dans une section critique en termes de performances, j’aimerais mettre en cache un délégué pour effectuer l’action. Le problème est que je n’arrive pas à me connecter à sa méthode avec Delegate.CreateDelegate. La structure en question n’est pas ma création et est utilisée en interaction avec une bibliothèque tierce. La structure en question ressemble à ceci:

public struct A { private int SomeMethod() { //body go here } } 

Et le code suivant échouera avec une “Erreur de liaison à la méthode cible”.

 Delegate.CreateDelegate(typeof(Func),typeof(A).GetMethod("SomeMethod",BindingFlags.Instance | BindingFlags.NonPublic)); 

Je sais que je peux écrire un arbre d’expression pour effectuer l’action, mais il semble étrange que je ne puisse pas utiliser mon goto normal pour ces choses avec la méthode Delegate.CreateDelegate .

Le code ci-dessus fonctionne très bien si A était une classe. Le problème ne se pose que parce que A est une structure. La documentation MSDN est incorrecte pour cette surcharge de CreateDelegate, car elle fonctionne avec des méthodes non statiques.

Problème intéressant. D’après ce rapport de bogue, il semble qu’il s’agisse d’un bogue qui sera corrigé dans une future version de .NET: http://connect.microsoft.com/VisualStudio/feedback/details/574959/cannot-create-open-instance -déléguer-à-valeur-types-méthodes-quelle-implémenter-une-interface # détails

EDIT: en fait, je pense que ce rapport de bogue concerne un problème différent, le comportement que vous observez peut donc ne pas être un bogue.

À partir de ce rapport de bogue, j’ai découvert qu’il existe une solution de contournement si vous spécifiez le premier argument de votre délégué comme étant passé par référence. Voici un exemple de travail complet:

 public struct A { private int _Value; public int Value { get { return _Value; } set { _Value = value; } } private int SomeMethod() { return _Value; } } delegate int SomeMethodHandler(ref A instance); class Program { static void Main(ssortingng[] args) { var method = typeof(A).GetMethod("SomeMethod", BindingFlags.Instance | BindingFlags.NonPublic); SomeMethodHandler d = (SomeMethodHandler)Delegate.CreateDelegate(typeof(SomeMethodHandler), method); A instance = new A(); instance.Value = 5; Console.WriteLine(d(ref instance)); } } 

EDIT: La réponse de Jon Skeet ici aborde également cette question.

Le premier paramètre d’un délégué de méthode d’instance indépendante ne peut pas être un type valeur. Cela est dû à la façon dont les types de valeur doivent être gérés lorsqu’ils sont utilisés en tant que parameters “this”. Vous ne pouvez pas simplement les transmettre par valeur (comme cela se produirait si vous passiez un type de valeur en tant que premier paramètre d’une méthode statique), la méthode agissant alors sur une copie et toute mutation de la copie n’ayant aucun effet sur l’original. .


UPDATE: comme indiqué dans une autre réponse, les types de valeur utilisés en tant que parameters ‘this’ sont effectivement transmis par référence.

Vous utilisez cette surcharge de CreateDelegate :

Crée un délégué du type spécifié pour représenter la méthode statique spécifiée.

SomeMethod n’est pas une méthode statique.

Utilisez une surcharge qui permet de spécifier un object cible :

 A target = new A(); Func f = (Func)Delegate.CreateDelegate( typeof(Func), target, typeof(A).GetMethod( "SomeMethod", BindingFlags.Instance | BindingFlags.NonPublic)); 

Cela signifie que vous devez créer un délégué pour chaque instance de A cependant. Vous ne pouvez pas réutiliser le même délégué pour différentes instances.

La meilleure solution consiste probablement à construire une expression Lambda à l’aide d’arbres d’expression LINQ:

 var p = Expression.Parameter(typeof(A), "arg"); var lambda = Expression.Lambda>( Expression.Call( p, typeof(A).GetMethod( "SomeMethod", BindingFlags.Instance | BindingFlags.NonPublic)), p); Func f = lambda.Comstack();