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 <> s та делегати є взаємозамінними, а це означає, що анонімні делегати та лямбда (які є синтаксичним цукром для анонімних делегатів) також можуть бути використані. Тому насправді не важливо, вказали ви параметр як Func <double, double> або делегат, який приймає подвійний і повертає подвійний. Єдина реальна перевага, яку дає вам названий делегат, - це можливість додавати коментарі xml-doc; описові назви так само легко реалізувати, як і назва параметра замість типу.
KeithS

3
Я б заперечував, що прототип методу все-таки робить код більш читабельним, ніж Func <x, y> - це не просто називання робить код читабельним, а, як ви кажете, що не заважає вам передавати лямбда в код.
Ян Джонсон

Якщо найменування типу делегата настільки важливе для ясності коду, я думаю, що в більшості випадків я б схилявся до інтерфейсу та реалізації.
квентин-зірин

@qstarin це метод не лише іменування делегата, але іменування аргументів, які має взяти метод, особливо якщо вони є лише рідними типами. Ви маєте рацію, я в основному використовую інтерфейси над делегатами
Ian Johnson

Якщо я роблю одну з них як загальну функцію, яка забезпечує загальну функціональність, (з не дуже поширеним типом повернення), все працює, поки я не спробую використовувати його з недійсним. Чи дійсно мені потрібно зробити дублікат функції, використовуючи Action замість Func, чи є кращий спосіб?
Dan Chase

175

У .Net (v2 та пізніших версіях) є кілька загальних типів, які дуже легко полегшують передачу функцій як делегатів.

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

І Func, і Action можуть бути оголошені таким чином, що мають від 0 до 4 параметрів. Наприклад, Func <double, int> приймає один параметр як параметр і повертає int. Дія <подвійне, подвійне, подвійне> приймає три параметри як параметри і нічого не повертає (недійсне).

Таким чином, ви можете оголосити свою функцію Diff, щоб взяти функцію:

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

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

Мені цікаво, чому параметр загального типу?
kdbanman

2
Я отримую цю помилку: аргументи типу для методу "Runner <T> (Func <T>)" не можна зробити з використання. Спробуйте точно вказати аргументи типу.
DermFrench

Який підпис методу "funcToRun"?
kravits88
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.