Передача функций-членов в С#

В большинстве случаев удобно, что делегаты С# уже хранят объект вместе с функцией-членом. Но есть ли способ сохранить и передать как параметры - только сама функция-член, так же, как хорошая старая функция-указатель на член в С++?

Если описание менее ясное, я даю автономный пример. И, да, в этом примере настойчивость, чтобы передать функции-члены, совершенно бессмысленна, но для этого у меня есть более серьезные возможности.

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

Видите ли, единственный обходной путь, который я нашел, довольно уродлив и, вероятно, медленный.

Ответы

Ответ 1

Использование существующего кода:

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

То, что вы хотите, называется открыть делегат экземпляра. Я написал о них в своем блоге

В принципе, вы можете создать делегат для метода экземпляра, не привязывая его к конкретному экземпляру, и указать экземпляр, который будет использовать его, когда вы его вызываете:

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

Ответ 3

Предполагая, что вы используете С# 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
    }
}

Ответ 4

Если вы согласны с передачей ссылки 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).

Ответ 5

Вы можете получить и повторно использовать 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 });    
 }

Я был бы очень осторожен, что это было действительно необходимо, поскольку мне кажется, что это запах кода.

Ответ 6

Вы можете сделать это таким образом

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

Ответ 7

Я думаю, вы можете сделать это легко, если я правильно понял:

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