2010-11-15 22 views
14

Principalmente è utile che i delegati C# memorizzino già l'oggetto insieme alla funzione membro. Ma c'è un modo, per memorizzare - e passare come parametri - solo la funzione membro stessa, proprio come la buona vecchia funzione pointer-to-member in C++?Passare intorno alle funzioni membro in C#

Nel caso in cui la descrizione sia meno chiara, fornisco un esempio autonomo. E, sì, nell'esempio l'insistenza a passare le funzioni dei membri è totalmente inutile, ma ho degli usi più seri per questo.

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<int, int> 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 (string [] 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(); 
    } 
} 

Vedete, l'unica soluzione che ho trovato è piuttosto brutto e probabilmente lento.

risposta

6

Prendendo il codice esistente:

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

Si potrebbe fare qualcosa di simile:

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

    return func(j); 
} 

questo creerà un nuovo delegato attorno al metodo e il nuovo oggetto. Per prendere il primo esempio:

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

Il tipo che stai cercando è System.Reflection.MethodInfo e sarebbe simile a questa:

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

    return func(i); 
} 

notare che, mentre si assegnano i delegati per ogni invocazione, credo che questa volontà essere ancora più veloce rispetto all'utilizzo della riflessione, dal momento che non è necessario inserire la funzione di input/output, né memorizzarla negli array object[].

2

È possibile recuperare e riutilizzare MethodInfo per il metodo o semplicemente utilizzare il nome ed estrarre il metodo in fase di esecuzione.

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

Sarei molto attenti che questo era effettivamente necessario come sembra come un codice odore per me.

3

Se stai bene passando il riferimento this come parametro, perché non utilizzare solo metodi statici?

class Foo { 
    public int i; 

    public static int ApplyHack(Foo foo, Func<Foo, int, int> 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)); 

Ciò riguarda principalmente come si costruisce l'oggetto Foo, senza cambiare come lo si utilizza. Inoltre, non ti impedisce di avere un metodo non statico int Multiply(int).

+0

Sicuramente più pulito ... tuttavia, se hai già il delegato, non hai bisogno del metodo 'ApplyHack' chiamarlo – cdhowie

+0

D'accordo, ma poiché la domanda suggerisce che c'è più di questo problema rispetto a quanto mostrato nell'esempio, ho cercato di mantenere la mia risposta simile. – Zooba

+0

Si noti che è anche possibile rinominare "Moltiplicare" su "Operatore *" per sovraccaricare l'operatore se ciò è più utile per voi. – Zooba

1

Si può fare in questo modo

class Foo 
{ 
    public int i { get; set; } 

    public static int Apply(Foo obj, Func<int, int, int> 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(string[] 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(); 
} 
1

Penso che si può fare facilmente con questo se ho capito bene:

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

e lo chiamo così:

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

Quello che vuoi è qualcosa chiamato open instance delegate.Ho scritto su di loro on my blog

In sostanza, è possibile creare un delegato a un metodo di istanza senza legare ad una particolare istanza, e specificare l'istanza di usarlo quando si chiama:

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 (string [] args) { 
     Func<Foo, int, int> multiply = (Func<Foo, int, int>)Delegate.CreateDelegate(typeof(Func<Foo, int, int>), null, typeof(Foo).GetMethod("Multiply"); 
     Func<Foo, int, int> add = (Func<Foo, int, int>)Delegate.CreateDelegate(typeof(Func<Foo, int, int>), 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(); 
    } 
} 
5

Supponendo che si sta utilizzando C# 2.0 o superiore, e avere accesso ai delegati anonimi, si può fare molto semplicemente avvolgendo la funzione in un delegato anonima al punto di stoccaggio:

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 
    } 
} 
Problemi correlati