Функция передачи C # в качестве аргумента

141

Я написал функцию в C #, которая выполняет численное дифференцирование. Это выглядит так:

public double Diff(double x)
{
    double h = 0.0000001;

    return (Function(x + h) - Function(x)) / h;
}

Я хотел бы быть в состоянии передать в любой функции, как в:

public double Diff(double x, function f)
{
    double h = 0.0000001;

    return (f(x + h) - f(x)) / h;
}

Я думаю, что это возможно с делегатами (возможно?), Но я не уверен, как их использовать.

Любая помощь будет принята с благодарностью.

ясень
источник

Ответы:

146

Использование Func, как упомянуто выше, работает, но есть также делегаты, которые выполняют ту же задачу и также определяют намерение в именовании:

public delegate double MyFunction(double x);

public double Diff(double x, MyFunction f)
{
    double h = 0.0000001;

    return (f(x + h) - f(x)) / h;
}

public double MyFunctionMethod(double x)
{
    // Can add more complicated logic here
    return x + 10;
}

public void Client()
{
    double result = Diff(1.234, x => x * 456.1234);
    double secondResult = Diff(2.345, MyFunctionMethod);
}
Ян Джонсон
источник
5
В 3.5 и более поздних версиях Func <> и делегаты являются взаимозаменяемыми, и это означает, что анонимные делегаты и лямбды (которые являются синтаксическим сахаром для анонимных делегатов) также могут использоваться. Так что на самом деле не имеет значения, указываете ли вы параметр как Func <double, double> или делегат, который принимает double и возвращает double. Единственное реальное преимущество, которое дает именованный делегат, - это возможность добавлять комментарии xml-doc; описательные имена так же легко реализовать, как и имя параметра вместо типа.
KeithS
3
Я бы сказал, что прототип метода по-прежнему делает код более читабельным, чем Func <x, y> - не только присвоение имен делает код читабельным, и, как вы говорите, не мешает передавать лямбды в код.
Ян Джонсон
Если наименование типа делегата так важно для ясности кода, я думаю, что в большинстве случаев я бы склонялся к интерфейсу и реализациям.
Квентин Старин
@qstarin - это не просто наименование делегата, но и аргументы, которые должен принимать метод, особенно если это просто нативные типы. Вы правы, в основном я использую интерфейсы над делегатами
Ian Johnson
Если я делаю одну из них как обычную функцию, которая обеспечивает общую функциональность (с не очень распространенным типом возврата), все работает, пока я не попытаюсь использовать ее с void. Мне действительно нужно сделать дублирующую функцию, используя Action вместо Func, или есть лучший способ?
Дэн Чейз
175

В .Net есть несколько общих типов (v2 и более поздние), которые очень легко передают функции делегатам.

Для функций с типами возврата есть Func <>, а для функций без типов возврата есть Action <>.

Можно объявить, что Func и Action принимают от 0 до 4 параметров. Например, Func <double, int> принимает один double в качестве параметра и возвращает int. Действие <double, double, double> принимает три типа double в качестве параметров и ничего не возвращает (void).

Таким образом, вы можете объявить свою функцию Diff для получения Func:

public double Diff(double x, Func<double, double> f) {
    double h = 0.0000001;

    return (f(x + h) - f(x)) / h;
}

И затем вы называете это так, просто давая ему имя функции, которая соответствует сигнатуре вашего Func или Action:

double result = Diff(myValue, Function);

Вы даже можете написать функцию в строке с лямбда-синтаксисом:

double result = Diff(myValue, d => Math.Sqrt(d * 3.14));
Квентин-starin
источник
29
В .NET 4 оба Funcи Actionбыли обновлены, чтобы учесть до 16 параметров.
Джоэл Мюллер
5
По-настоящему крутой вещью было бы вернуть a Func<double, double>, являющееся первой производной функции ввода, вычисленной, конечно, численно. return x => (f(x + h) - f(x)) / h;Вы могли бы даже написать перегрузку, которая возвращала бы nпроизводную функции ввода.
Ани
15
public static T Runner<T>(Func<T> funcToRun)
{
    //Do stuff before running function as normal
    return funcToRun();
}

Использование:

var ReturnValue = Runner(() => GetUser(99));
kravits88
источник
Мне интересно, почему параметр универсального типа?
kdbanman
2
Я получаю эту ошибку: Аргументы типа для метода Runner <T> (Func <T>) не могут быть выведены из использования. Попробуйте указать аргументы типа явно.
DermFrench
Какая у вас подпись метода "funcToRun"?
kravits88