2010-11-15 110 views
14

大多數情況下,C#委託人已經將對象與成員函數一起存儲了。但是有沒有辦法,像C++中的舊指針 - 成員函數一樣,存儲 - 並傳遞參數 - 只有成員函數本身?在C#中傳遞成員函數#

如果描述不夠清晰,我給出一個獨立的示例。而且,是的,在這個例子中,堅持傳遞成員函數是毫無意義的,但我對此有更多的認真使用。

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(); 
    } 
} 

你看,我發現的唯一的解決方法是相當醜陋,可能很慢。

回答

6

以你的現有代碼:

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

你可以做這樣的事情:

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); 
} 

這將創建一個圍繞的方法和新的對象一個新的委託。把你的第一個例子:

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

你正在尋找的類型是System.Reflection.MethodInfo,它應該是這樣的:

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); 
} 

注意,當你在每次調用分配的代表,我相信,這將仍然比使用反射更快,因爲您不必輸入/輸出框函數,也不會將其存儲在object[]陣列中。

2

您可以檢索並重新使用該方法的MethodInfo,或只使用該名稱並在運行時提取該方法。

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

我會非常小心,這實際上是必要的,因爲它看起來像一個代碼味道給我。

3

如果您將this引用作爲參數傳遞,那麼爲什麼不使用靜態方法呢?

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)); 

這主要影響如何構造Foo對象,而不改變如何使用它。它也不會阻止你有一個非靜態的int Multiply(int)方法。

+0

絕對清潔......但是,如果你已經有了委託,你不需要'ApplyHack'方法稱它。 – cdhowie 2010-11-15 20:45:32

+0

同意,但由於問題表明這個問題比例子中顯示的更多,我試着保持我的答案類似。 – Zooba 2010-11-15 20:47:52

+0

請注意,如果這對您更有用,您也可以將'Multiply'重命名爲'operator *'以使操作符超載。 – Zooba 2010-11-15 20:48:38

1

你可以做到這樣

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

我認爲你可以,如果我理解正確的話很容易做到這一點:

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

,並調用它像這樣:

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

你想要什麼叫做open instance delegate。我寫關於他們on my blog

基本上,你可以創建一個委託實例方法,而不將其直接連接到特定的實例,並指定實例來使用它,當你調用它:

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

假設您使用的是C#2.0或更高版本,並且有權訪問匿名代理,則可以通過在存儲位置將函數包裝在匿名代理中執行此操作:

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