在C#中传递成员函数

时间:2010-11-15 20:37:49

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

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

7 个答案:

答案 0 :(得分:10)

你想要的是一种叫做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 ();
    }
}

答案 1 :(得分:7)

使用现有代码:

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 :(得分: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
    }
}

答案 3 :(得分: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)方法。

答案 4 :(得分: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 });    
 }

我会非常小心,这实际上是必要的,因为它看起来像是一种代码味道。

答案 5 :(得分: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();
}

答案 6 :(得分: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));