Passage autour des fonctions membres en C #

Généralement, il est pratique que les delegates C # stockent déjà l’object avec la fonction membre. Mais existe-t-il un moyen de stocker – et de passer en tant que parameters – uniquement la fonction membre elle-même, tout comme le bon vieux pointeur vers la fonction membre en C ++?

Si la description est moins que claire, je donne un exemple autonome. Et, oui, dans l’exemple, insister pour faire passer les fonctions des membres est totalement inutile, mais j’ai des utilisations plus sérieuses à cet égard.

class Foo { public int i { get; set; } /* Can this be done? public static int Apply (Foo obj, ???? method, int j) { return obj.method (j); } */ public static int ApplyHack (Foo obj, Func method, int j) { return (int) method.Method.Invoke (obj, new object [] { j }); } public static readonly Foo _ = new Foo (); // dummy object for ApplyHack public int Multiply (int j) { return i * j; } public int Add (int j) { return i + j; } } class Program { static void Main (ssortingng [] args) { var foo = new Foo { i = 7 }; Console.Write ("{0}\n", Foo.ApplyHack (foo, Foo._.Multiply, 5)); Console.Write ("{0}\n", Foo.ApplyHack (foo, Foo._.Add, 5)); Console.ReadKey (); } } 

Vous voyez, la seule solution que j’ai trouvée est plutôt laide et probablement lente.

Prenant votre code existant:

 public static int ApplyHack (Foo obj, Func method, int j) { return (int) method.Method.Invoke (obj, new object [] { j }); } 

Vous pouvez faire quelque chose comme ça:

 public static int ApplyHack (Foo obj, Func method, int j) { var func = (Func)Delegate.CreateDelegate(typeof(Func), obj, method.Method); return func(j); } 

Cela créera un nouveau délégué autour de la méthode et du nouvel object. Pour prendre votre premier exemple:

 public static int Apply (Foo obj, ???? method, int j) { return obj.method (j); } 

Le type que vous recherchez est System.Reflection.MethodInfo et ressemble à ceci:

 public static int Apply (Foo obj, MethodInfo method, int j) { var func = (Func)Delegate.CreateDelegate(typeof(Func), obj, method); return func(i); } 

Notez que si vous allouez des delegates pour chaque appel, je pense que cela sera toujours plus rapide que l’utilisation de la reflection, car vous n’avez pas à cocher la fonction input / output, ni à la stocker dans des tableaux object[] .

Ce que vous voulez, c’est ce qu’on appelle un délégué d’instance ouverte . J’ai écrit à leur sujet sur mon blog

En gros, vous pouvez créer un délégué pour une méthode d’instance sans la lier à une instance particulière, et spécifier l’instance sur laquelle l’utiliser lorsque vous l’appelez:

 class Foo { public int i { get; set; } public int Multiply (int j) { return i * j; } public int Add (int j) { return i + j; } } class Program { static void Main (ssortingng [] args) { Func multiply = (Func)Delegate.CreateDelegate(typeof(Func), null, typeof(Foo).GetMethod("Multiply"); Func add = (Func)Delegate.CreateDelegate(typeof(Func), null, typeof(Foo).GetMethod("Add"); var foo1 = new Foo { i = 7 }; var foo2 = new Foo { i = 8 }; Console.Write ("{0}\n", multiply(foo1, 5)); Console.Write ("{0}\n", add(foo1, 5)); Console.Write ("{0}\n", multiply(foo2, 5)); Console.Write ("{0}\n", add(foo2, 5)); Console.ReadKey (); } } 

En supposant que vous utilisez C # 2.0 ou supérieur et que vous avez access à des delegates anonymes, vous pouvez le faire très simplement en encapsulant la fonction dans un délégué anonyme au sharepoint stockage:

 class Foo { public Foo(int v) { this.v = v; } int v; public int Multiply(int x) { return v * x; } public int Add(int x) { return v+x; } delegate int NewFunctionPointer(Foo, int); delegate int OldDelegateStyle(int); static void Example() { Foo f = new Foo(2); Foo f2 = new Foo(3); // instead of this, which binds an instance OldDelegateStyle oldMul = f.Multiply; // You have to use this NewFunctionPointer mul = delegate(Foo f, int x) { return f.Multiply(x); } NewFunctionPointer add = delegate(Foo f, int x) { return f.Add(x); } // But can now do this mul(f, 4); // = 8 add(f2, 1); // = 3 } } 

Si vous acceptez de passer la référence this tant que paramètre, pourquoi ne pas simplement utiliser des méthodes statiques?

 class Foo { public int i; public static int ApplyHack(Foo foo, Func method, int j) { return method(foo, j); } public static int Multiply(Foo foo, int j) { return foo.i * j; } } Console.Write("{0}\n", Foo.ApplyHack(foo, Foo.Multiply, 5)); 

Cela affecte principalement la manière dont vous construisez l’object Foo , sans changer son utilisation. Cela ne vous empêche pas non plus d’avoir une méthode int Multiply(int) non statique.

Vous pouvez récupérer et réutiliser MethodInfo pour la méthode ou simplement utiliser le nom et extraire la méthode au moment de l’exécution.

  public static int ApplyHack (Foo obj, ssortingng methodName, int j) { var method = typeof(Foo).GetMethod(methodName); return (int) method.Invoke (obj, new object [] { j }); } 

Je ferais très attention à ce que cela soit réellement nécessaire car cela ressemble à une odeur de code pour moi.

Vous pouvez le faire de cette façon

 class Foo { public int i { get; set; } public static int Apply(Foo obj, Func method, int j) { return method(j, obj.i); } public static int Multiply(int j, int i) { return i * j; } public static int Add(int j, int i) { return i + j; } } static void Main(ssortingng[] args) { var foo = new Foo { i = 7 }; Console.Write("{0}\n", Foo.Apply(foo, Foo.Multiply, 5)); Console.Write("{0}\n", Foo.Apply(foo, Foo.Add, 5)); Console.ReadKey(); } 

Je pense que vous pouvez le faire facilement avec ceci si je comprends bien:

 public static int Apply(Func method, int j) { return (int)method.Method.Invoke(method.Target, new object[] { j }); } 

et appelez comme ça:

 Console.Write("{0}\n", Foo.Apply(foo.Multiply, 5));