Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing around member functions in C#

Tags:

c#

Mostly it comes handy that C# delegates already store the object together with 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.

like image 244
Pjacobi Avatar asked Nov 15 '10 20:11

Pjacobi


4 Answers

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 ();
    }
}
like image 170
thecoop Avatar answered Sep 26 '22 01:09

thecoop


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.

like image 32
cdhowie Avatar answered Sep 26 '22 01:09

cdhowie


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
    }
}
like image 32
Nicholas M T Elliott Avatar answered Sep 25 '22 01:09

Nicholas M T Elliott


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.

like image 25
Zooba Avatar answered Sep 22 '22 01:09

Zooba