Как обернуть Funcс неизвестным номером и типом параметров в C #? - PullRequest
1 голос
/ 07 ноября 2019

Предположим, у меня есть этот класс:

public class Function {
    public int argc;        //number of arguments of the function
    public float[] argv;
    public Func<float> f;   //attribute to store a function (e.g. Sin(x) or Pow(a, b))
}

Я хочу создать экземпляры Function, которые содержат различные функции, такие как Sin(x) или Pow(a, b), но я не знаю, как связыватьсуществующая функция (с любым количеством аргументов) до Func. Очевидно, его объявление не всегда будет Func<float>, но Func<float, float>, Func<float, float, float> и т. Д.

Я искал Func, delegate, Action, но все еще не понялкак получить эту «капсулу функций», которая может содержать и выполнять функции с разным количеством аргументов. Для простоты я считаю, что единственный тип ввода и вывода - float.

Я думаю об использовании чего-то вроде Func<List<float>>, но я хочу знать, есть ли лучший вариант.

1 Ответ

2 голосов
/ 07 ноября 2019

Я хочу предложить ответ, который более точно соответствует сценарию, описанному в ОП. Ключ в использовании Delegate.DynamicInvoke, который позволяет передавать делегату неограниченное количество аргументов.

public class Function<TReturn> {
    private readonly object[] _argv;

    private readonly Delegate _func;

    public Function(Delegate func, params object[] args) {
        _func = func;
        _argv = args;
    }

    public TReturn Run() {
        object v = _func.DynamicInvoke(_argv);
        return (TReturn)v;
    }
}

А его использование позволяет вам динамически определять количество аргументов, которые вы хотите передать:

var s = new Function<double>((Func<double, double>)(x => Math.Sin(x)), 1 );
Console.WriteLine(s.Run()); // prints 0.8414709848078965

var p = new Function<double>((Func<double, double, double>)((a, b) => Math.Pow(a, b)), 2, 3);
Console.WriteLine(p.Run()); // prints 8

var d = new Function<string>((Func<string, double, string>)((a, b) => a + b.ToString()), "hello, ", 42);
Console.WriteLine(p.Run()); // prints "hello, 42"

Обратите внимание, что проверка типов выполняется только во время выполнения при вызове Function.Run(), а не при создании объекта Function из-за его динамической природы. Если вы точно знаете, что все переданные аргументы всегда будут одного типа, вы можете применить это статически, добавив TArg универсальный тип.

...