2010-09-01 6 views
93

J'ai écrit une fonction en C# qui fait une différenciation numérique. Il ressemble à ceci:C# Passing Function as Argument

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

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

Je voudrais pouvoir passer dans toutes les fonctions, comme dans:

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

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

Je pense que cela est possible avec les délégués (?) Peut-être, mais je ne suis pas sûr comment les utiliser.

Toute aide serait grandement apprécié.

Répondre

102

aide le Func comme mentionné ci-dessus fonctionne, mais il y a aussi des délégués qui font la même tâche et définissent également l'intention dans le nommage:

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

En 3.5 et plus tard, les Func <> s et les délégués sont interchangeables, ce qui signifie que les délégués anonymes et les lambdas (qui sont du sucre syntaxique pour les délégués anonymes) peuvent également être utilisés. Donc, cela n'a pas vraiment d'importance si vous spécifiez le paramètre comme Func ou un délégué qui prend un double et renvoie un double. Le seul véritable avantage d'un délégué nommé est la possibilité d'ajouter des commentaires xml-doc; les noms descriptifs sont aussi faciles à implémenter que le nom du paramètre au lieu du type. – KeithS

+3

Je dirais que le prototype de méthode rend encore le code plus lisible que le Func - ce n'est pas seulement le nommage qui rend le code lisible et comme vous le dites, cela ne vous empêche pas de passer lambdas dans le code. –

+0

Si la dénomination du type de délégué est si importante pour la clarté du code, je pense que dans la plupart des cas je pencherais vers une interface et des implémentations. –

138

Il existe deux types génériques dans .Net (v2 et versions ultérieures) qui facilitent le passage des fonctions en tant que délégués.

Pour les fonctions avec types de retour, il y a Func <> et pour les fonctions sans types de retour il y a l'action <>.

Func et Action peuvent être déclarés pour prendre de 0 à 4 paramètres. Par exemple, Func < double, int> prend un double comme paramètre et retourne un int. Action < double, double, double> prend trois doubles comme paramètres et ne renvoie rien (vide).

Vous pouvez déclarer votre fonction Diff de prendre un Func:

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

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

Et puis vous l'appeler comme cela, en donnant simplement le nom de la fonction qui correspond à la signature de votre Func ou Action:

double result = Diff(myValue, Function); 

Vous pouvez même écrire la fonction en ligne avec la syntaxe lambda:

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

Dans .NET 4, les deux 'Func 'et' Action' ont été mis à jour pour autoriser les paramètres [jusqu'à 16] (http://msdn.microsoft.com/en-us/library/dd402862.aspx). –

+4

Une chose vraiment cool à faire serait de retourner un 'Func ' qui est le premier dérivé de la fonction d'entrée, calculé numériquement bien sûr. 'return x => (f (x + h) - f (x))/h;' Vous pourriez même écrire une surcharge qui renvoie la dérivée 'n' de la fonction d'entrée. – Ani

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

Utilisation:

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

Je suis curieux, pourquoi le paramètre de type générique? – kdbanman

+1

Je reçois cette erreur: Les arguments de type pour la méthode 'Runner (Func )' ne peuvent pas être déduits de l'utilisation. Essayez de spécifier explicitement les arguments de type. – DermFrench

+0

Quelle est la signature de votre méthode "funcToRun"? – kravits88