2010-09-01 7 views
93

Napisałem funkcję w języku C#, która dokonuje numerycznego rozróżnienia. Wygląda to tak:C# Funkcja przechodzenia jako argument

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

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

Chciałbym być w stanie przejść w dowolnej funkcji, na przykład:

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

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

myślę, że to jest możliwe? Delegaci (być może), ale nie jestem pewnie, jak z nich korzystać.

Każda pomoc zostanie bardzo doceniona.

Odpowiedz

102

Używanie FUNC jak wspomniano wyżej prac, ale są też delegaci, które robią to samo zadanie, a także określić intencję w nazewnictwie:

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); 
} 
+4

W wersji 3.5 i nowszych funkcje <> i delegatów są wymienialne, co oznacza, że ​​można również używać anonimowych delegatów i lambd (które są syntaktycznym cukrem dla anonimowych delegatów). Nie ma więc znaczenia, czy parametr zostanie określony jako Func , czy delegat, który bierze podwójne i zwraca podwójne. Jedyną realną zaletą, którą daje wymieniony delegat, jest możliwość dodawania komentarzy do xml-doc; opisowe nazwy są równie łatwe do wdrożenia, jak nazwa parametru zamiast typu. – KeithS

+3

Twierdzę, że prototyp metody nadal sprawia, że ​​kod jest bardziej czytelny niż Func - nie jest to tylko nazewnictwo, które sprawia, że ​​kod jest czytelny i jak mówisz, nie przeszkadza ci to w przekazywaniu kodów lambda do kodu. –

+0

Jeśli nazewnictwo typu delegata jest tak ważne dla jasności kodu, myślę, że w większości przypadków byłbym skłonny do interfejsu i implementacji. –

138

Istnieje kilka typów ogólnych w .Net (v2 i później), które sprawiają, że przekazywanie funkcji jako delegatów jest bardzo łatwe.

W przypadku funkcji z typami zwrotów istnieje Func <> a w przypadku funkcji bez typów powrotu dostępne jest działanie <>.

Zarówno funkcje Func, jak i Action mogą być zadeklarowane jako od 0 do 4 parametrów. Na przykład, Func < double, int> przyjmuje jeden podwójny jako parametr i zwraca int. Działanie < podwójne, podwójne, podwójne> przyjmuje trzy podwójne jako parametry i nic nie zwraca (void).

Więc można zadeklarować swoją funkcję Diff podjąć Func:

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

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

A potem to nazwać jak tak, to po prostu nadając mu nazwę funkcji, która pasuje podpis swojej Func lub działania:

double result = Diff(myValue, Function); 

można nawet napisać funkcję w linii ze składnią lambda:

double result = Diff(myValue, d => Math.Sqrt(d * 3.14)); 
+23

W .NET 4, zarówno 'Func 'i' Action' zostały zaktualizowane, aby umożliwić parametry [do 16] (http://msdn.microsoft.com/en-us/library/dd402862.aspx). –

+4

Naprawdę fajną rzeczą byłoby zwrócić 'Func ', która jest pierwszą pochodną funkcji wejściowej, oczywiście obliczoną liczbowo. 'return x => (f (x + h) - f (x))/h;' Można nawet napisać przeciążenie, które zwróciło 'n' pochodną funkcji wejściowej. – Ani

7
public static T Runner<T>(Func<T> funcToRun) 
{ 
    //Do stuff before running function as normal 
    return funcToRun(); 
} 

Zastosowanie:

var ReturnValue = Runner(() => GetUser(99)); 
+0

Jestem ciekawy, dlaczego ogólny parametr typu? – kdbanman

+1

Otrzymuję ten błąd: argumentów typu dla metody "Runner (Func )" nie można wywnioskować z użycia. Spróbuj jawnie określić argumenty typu. – DermFrench

+0

Jaki jest twój podpis metody "funcToRun"? – kravits88