Je me retrouve souvent à écrire des fonctions qui ressemblent à ceci parce qu'elles me permettent de simuler facilement l'accès aux données, tout en fournissant une signature qui accepte les paramètres pour déterminer les données auxquelles accéder.
public static string GetFormattedRate(
Func<string, RateType>> getRate,
string rateKey)
{
var rate = getRate(rateKey);
var formattedRate = rate.DollarsPerMonth.ToString("C0");
return formattedRate;
}
Ou
public static string GetFormattedRate(
Func<RateType, string> formatRate,
Func<string, RateType>> getRate,
string rateKey)
{
var rate = getRate(rateKey);
var formattedRate = formatRate(rate);
return formattedRate;
}
Ensuite, je l'utilise quelque chose comme ceci:
using FormatterModule;
public static Main()
{
var getRate = GetRateFunc(connectionStr);
var formattedRate = GetFormattedRate(getRate, rateType);
// or alternatively
var formattedRate = GetFormattedRate(getRate, FormatterModule.FormatRate, rateKey);
System.PrintLn(formattedRate);
}
Est-ce une pratique courante? Je sens que je devrais faire quelque chose de plus
public static string GetFormattedRate(
Func<RateType> getRate())
{
var rate = getRate();
return rate.DollarsPerMonth.ToString("C0");
}
Mais cela ne semble pas très bien fonctionner car je devrais créer une nouvelle fonction pour passer dans la méthode pour chaque type de taux.
Parfois, je sens que je devrais faire
public static string GetFormattedRate(RateType rate)
{
return rate.DollarsPerMonth.ToString("C0");
}
Mais cela semble supprimer toute possibilité de récupération et de formatage. Chaque fois que je veux récupérer et formater, je dois écrire deux lignes, une à récupérer et une à formater.
Qu'est-ce qui me manque dans la programmation fonctionnelle? Est-ce la bonne façon de le faire ou existe-t-il un meilleur modèle à la fois facile à entretenir et à utiliser?
la source
GetFormattedRate()
d'accepter le taux à formater comme paramètre, plutôt que de lui faire accepter une fonction qui renvoie le taux à formater comme paramètre?closures
endroit où vous passez le paramètre lui-même à une fonction, ce qui en retour vous donne une fonction faisant référence à ce paramètre spécical. Cette fonction "configurée" serait passée en paramètre à la fonction qui l'utilise.Réponses:
Si vous faites cela assez longtemps, vous finirez par vous retrouver à écrire cette fonction encore et encore:
Félicitations, vous avez inventé la composition des fonctions .
Les fonctions d'encapsuleur comme celle-ci n'ont pas beaucoup d'utilité lorsqu'elles sont spécialisées dans un seul type. Cependant, si vous introduisez des variables de type et omettez le paramètre d'entrée, votre définition GetFormattedRate ressemble à ceci:
Dans l'état actuel des choses, ce que vous faites a peu d'utilité. Ce n'est pas générique, vous devez donc dupliquer ce code partout. Il sur-complique votre code car maintenant votre code doit assembler tout ce dont il a besoin à partir de mille petites fonctions. Mais votre cœur est au bon endroit: il vous suffit de vous habituer à utiliser ces sortes de fonctions génériques d'ordre supérieur pour assembler les choses. Ou, utilisez un bon lambda à l'ancienne pour transformer
Func<A, B>
etA
devenirFunc<B>
.Ne te répète pas.
la source
FormatRate(GetRate(rateKey))
.GetFormattedRate
désormais utiliser directement.Func<Func<A, B>, C>
); cela signifie que vous n'avez besoin que d'une seule fonction de composition qui fonctionne pour n'importe quelle fonction. Cependant, vous pouvez assez bien travailler avec les fonctions C # en utilisant simplement des fermetures - au lieu de passerFunc<rateKey, rateType>
, vous avez vraiment besoinFunc<rateType>
, et lorsque vous passez le func, vous le construisez comme() => GetRate(rateKey)
. Le fait est que vous n'exposez pas les arguments dont la fonction cible ne se soucie pas.Compose
fonction n'est vraiment utile que si vous devez retarder l'exécution deGetRate
pour une raison quelconque, comme si vous souhaitez passerCompose(FormatRate, GetRate)
à une fonction qui fournit un taux de son choix, par exemple pour l'appliquer à chaque élément d'un liste.Il n'y a absolument aucune raison de passer une fonction et ses paramètres, puis de l'appeler avec ces paramètres. En fait, dans votre cas , vous avez aucune raison de passer une fonction du tout . L'appelant pourrait tout aussi bien appeler la fonction elle-même et transmettre le résultat.
Pensez-y - au lieu d'utiliser:
pourquoi ne pas simplement utiliser:
?
En plus de réduire le code inutile, il réduit également le couplage - si vous voulez changer la façon dont le taux est récupéré (par exemple, s'il a
getRate
maintenant besoin de deux arguments), vous n'avez pas à le changerGetFormattedRate
.De même, il n'y a aucune raison d'écrire
GetFormattedRate(formatRate, getRate, rateKey)
au lieu d'écrireformatRate(getRate(rateKey))
.Ne compliquez pas les choses.
la source
formatRate
qui devrait être mappé sur les taux qui devraient être formatés).Si vous devez absolument passer une fonction dans la fonction car elle passe un argument supplémentaire ou l'appelle dans une boucle, vous pouvez plutôt passer un lambda:
Le lambda liera les arguments que la fonction ne connaît pas et cachera qu'ils existent même.
la source
N'est-ce pas ce que tu veux?
Et puis appelez-le comme ceci:
Si vous voulez une méthode qui peut se comporter de plusieurs manières différentes dans un langage orienté objet tel que C #, la façon habituelle de le faire est de faire en sorte que la méthode appelle une méthode abstraite. Si vous n'avez pas de raison spécifique de le faire d'une manière différente, vous devez le faire de cette façon.
Cela ressemble-t-il à une bonne solution ou y a-t-il des inconvénients auxquels vous pensez?
la source
GetFormattedRate
méthode et simplement appelerIRateFormatter.FormatRate(rate)
). Le concept de base est cependant correct, et je pense que trop OP devrait implémenter son code polymorphiquement s'il a besoin de plusieurs méthodes de formatage.