Passing around member functions in C#
Mostly it comes handy that C# delegates already store the object together wit开发者_StackOverflowh the member function. But is there a way, to store -- and pass as parameters -- only the member function itself, just as the good old pointer-to-member-function in C++?
In case the description is less than clear, I give a self-contained example. And, yes, in the example the insistence to pass around member functions is totally pointless, but I have more serious uses for this.
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 ();
}
}
You see, the only workaround I've found is rather ugly and probably slow.
What you want is something called an open instance delegate. I've written about them on my blog
Basically, you can create a delegate to an instance method without tying it to a particular instance, and specify the instance to use it on when you call it:
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 ();
}
}
Taking your existing code:
public static int ApplyHack (Foo obj, Func<int, int> method, int j) {
return (int) method.Method.Invoke (obj, new object [] { j });
}
You could do something like this:
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);
}
This will create a new delegate around the method and the new object. To take your first example:
public static int Apply (Foo obj, ???? method, int j) {
return obj.method (j);
}
The type you are looking for is System.Reflection.MethodInfo
and it would look like this:
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);
}
Note that while you are allocating delegates for each invocation, I believe this will still be faster than using reflection, since you do not have to box function input/output, nor store it in object[]
arrays.
Assuming you're using C# 2.0 or above, and have access to anonymous delegates, you can do it very simply by wrapping the function in an anonymous delegate at the point of storage:
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
}
}
If you're okay with passing the this
reference as a parameter, why not just use static methods?
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));
This mainly affects how you construct the Foo
object, without changing how you use it. It also doesn't prevent you from having a non-static int Multiply(int)
method.
You could retrieve and reuse the MethodInfo for the method or just use the name and extract the method at runtime.
public static int ApplyHack (Foo obj, string methodName, int j)
{
var method = typeof(Foo).GetMethod(methodName);
return (int) method.Invoke (obj, new object [] { j });
}
I'd be very careful that this was actually necessary as it seems like a code smell to me.
You can do it that way
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();
}
I think you can do this easily with this if I understand correctly:
public static int Apply(Func<int, int> method, int j)
{
return (int)method.Method.Invoke(method.Target, new object[] { j });
}
and call it like this:
Console.Write("{0}\n", Foo.Apply(foo.Multiply, 5));
精彩评论