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

你看,我发现的唯一的解决方法是相当丑陋,可能很慢。

使用您现有的代码:

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

你可以这样做:

 public static int ApplyHack (Foo obj, Func method, int j) { var func = (Func)Delegate.CreateDelegate(typeof(Func), 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)Delegate.CreateDelegate(typeof(Func), obj, method); return func(i); } 

请注意,在为每次调用分配委托时,我相信这仍然比使用reflection更快,因为您不必使用框函数输入/输出,也不将它存储在object[]数组中。

你想要的是一个叫做开放实例委托的东西。 我在博客上写过这些文章

基本上,您可以创建实例方法的委托而不将其绑定到特定实例,并在调用它时指定要在其上使用它的实例:

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

假设您正在使用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 } } 

如果您可以this引用作为参数传递,为什么不使用静态方法呢?

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

这主要影响您构造Foo对象的方式,而不会改变您使用它的方式。 它也不会阻止您使用非静态int Multiply(int)方法。

您可以检索并重用方法的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 }); } 

我会非常小心,这实际上是必要的,因为它似乎对我来说是一种代码味道。

你可以这样做

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

如果我理解正确的话,我想你可以轻松地做到这一点:

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

并称之为:

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