2010-11-15 59 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 
    } 
}