C # Funzione passante come argomento


141

Ho scritto una funzione in C # che fa una differenziazione numerica. Sembra così:

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

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

Vorrei poter passare a qualsiasi funzione, come in:

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

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

Penso che questo sia possibile con i delegati (forse?) Ma non sono sicuro di come usarli.

Qualsiasi aiuto sarebbe molto apprezzato.

Risposte:


146

L'uso di Func come menzionato sopra funziona ma ci sono anche delegati che svolgono la stessa attività e definiscono anche l'intento nella denominazione:

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
In 3.5 e versioni successive, Func <> se delegati sono intercambiabili e ciò significa che è possibile utilizzare anche delegati anonimi e lambda (che sono zucchero sintattico per delegati anonimi). Quindi non importa se si specifica il parametro come Func <double, double> o delegato che accetta un double e restituisce un double. L'unico vero vantaggio che ti offre un delegato nominato è la possibilità di aggiungere commenti xml-doc; i nomi descrittivi sono altrettanto facili da implementare come il nome del parametro anziché il tipo.
KeithS

3
Direi che il prototipo del metodo rende ancora il codice più leggibile rispetto a Func <x, y> - non è solo la denominazione che rende leggibile il codice e, come dici tu, non ti impedisce di passare lambda nel codice.
Ian Johnson,

Se la denominazione del tipo delegato è così importante per la chiarezza del codice, penso che nella maggior parte dei casi mi spingerei verso un'interfaccia e le implementazioni.
Quentin-starin,

@qstarin non è solo la denominazione del delegato, ma la denominazione degli argomenti che il metodo deve prendere, specialmente se sono solo tipi nativi. Hai ragione, per lo più uso interfacce su delegati
Ian Johnson,

Se sto facendo uno di questi come una funzione comune che fornisce funzionalità comuni, (con un tipo di ritorno non così comune) tutto funziona finché non provo ad usarlo con il vuoto. Devo davvero fare una funzione duplicata usando Action invece di Func o c'è un modo migliore?
Dan Chase,

175

Esistono un paio di tipi generici in .Net (v2 e versioni successive) che rendono molto semplice il passaggio di funzioni come delegati.

Per le funzioni con tipi di restituzione, è presente <> e per le funzioni senza tipi di restituzione è presente <>.

Sia Func che Action possono essere dichiarati per accettare da 0 a 4 parametri. Ad esempio, Func <double, int> accetta un doppio come parametro e restituisce un int. L'azione <double, double, double> accetta tre doppie come parametri e non restituisce nulla (nulla).

Quindi puoi dichiarare la tua funzione Diff per prendere un Func:

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

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

E poi lo chiami così, semplicemente assegnandogli il nome della funzione che si adatta alla firma del tuo Func o Action:

double result = Diff(myValue, Function);

Puoi persino scrivere la funzione in linea con la sintassi lambda:

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

29
In .NET 4, entrambi Funce Actionsono stati aggiornati per consentire fino a 16 parametri.
Joel Mueller,

5
Una cosa davvero interessante da fare sarebbe quella di restituire una Func<double, double>che è la prima derivata della funzione di input, calcolata numericamente ovviamente. return x => (f(x + h) - f(x)) / h;Si potrebbe anche scrivere un sovraccarico che ha restituito la nderivata th della funzione di input.
Ani,

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

Uso:

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

Sono curioso, perché il parametro di tipo generico?
kdbanman,

2
Ricevo questo errore: l'argomento tipo per il metodo 'Runner <T> (Func <T>)' non può essere dedotto dall'uso. Prova a specificare esplicitamente gli argomenti del tipo.
DermFrench,

Qual è la tua firma del metodo "funcToRun"?
kravits88,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.