Oft schreibe ich Funktionen, die so aussehen, weil ich damit leicht den Datenzugriff verspotten und trotzdem eine Signatur bereitstellen kann, die Parameter akzeptiert, um zu bestimmen, auf welche Daten zugegriffen werden soll.
public static string GetFormattedRate(
Func<string, RateType>> getRate,
string rateKey)
{
var rate = getRate(rateKey);
var formattedRate = rate.DollarsPerMonth.ToString("C0");
return formattedRate;
}
Oder
public static string GetFormattedRate(
Func<RateType, string> formatRate,
Func<string, RateType>> getRate,
string rateKey)
{
var rate = getRate(rateKey);
var formattedRate = formatRate(rate);
return formattedRate;
}
Dann benutze ich so etwas:
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);
}
Ist das eine gängige Praxis? Ich fühle mich wie ich etwas tun sollte
public static string GetFormattedRate(
Func<RateType> getRate())
{
var rate = getRate();
return rate.DollarsPerMonth.ToString("C0");
}
Das scheint aber nicht sehr gut zu funktionieren, da ich eine neue Funktion erstellen müsste, um für jeden Kurstyp in die Methode zu übergehen.
Manchmal habe ich das Gefühl, ich sollte es tun
public static string GetFormattedRate(RateType rate)
{
return rate.DollarsPerMonth.ToString("C0");
}
Dies scheint jedoch die Wiederverwendbarkeit von Abrufen und Formaten zu beeinträchtigen. Wann immer ich abrufen und formatieren möchte, muss ich zwei Zeilen schreiben, eine zum Abrufen und eine zum Formatieren.
Was vermisse ich an funktionaler Programmierung? Ist dies der richtige Weg, oder gibt es ein besseres Muster, das sowohl einfach zu warten als auch zu verwenden ist?
quelle
GetFormattedRate()
, die zu formatierende Rate als Parameter zu akzeptieren, als eine Funktion zu akzeptieren, die die zu formatierende Rate als Parameter zurückgibt.closures
wo Sie den Parameter selbst an eine Funktion übergeben, die Ihnen im Gegenzug eine Funktion gibt, die sich auf diesen bestimmten Parameter bezieht. Diese "konfigurierte" Funktion würde als Parameter an die Funktion übergeben, die sie verwendet.Antworten:
Wenn Sie dies lange genug tun, werden Sie irgendwann feststellen, dass Sie diese Funktion immer wieder schreiben:
Herzlichen Glückwunsch, Sie haben die Funktionszusammensetzung erfunden .
Wrapper-Funktionen wie diese haben wenig Sinn, wenn sie auf einen Typ spezialisiert sind. Wenn Sie jedoch einige Typvariablen einführen und den Eingabeparameter weglassen, sieht Ihre GetFormattedRate-Definition folgendermaßen aus:
So wie es aussieht, hat das, was Sie tun, wenig Sinn. Es ist nicht generisch, daher müssen Sie diesen Code überall duplizieren. Es macht Ihren Code überkompliziert, weil Ihr Code jetzt alles, was er benötigt, aus tausend kleinen Funktionen zusammensetzen muss. Ihr Herz ist jedoch an der richtigen Stelle: Sie müssen sich nur daran gewöhnen, diese Art von generischen Funktionen höherer Ordnung zu verwenden, um Dinge zusammenzusetzen. Oder verwenden Sie ein gutes altes Mode - Lambda zu drehen
Func<A, B>
undA
inFunc<B>
.Wiederhole dich nicht.
quelle
FormatRate(GetRate(rateKey))
.GetFormattedRate
von nun an direkt verwenden kann.Func<Func<A, B>, C>
); Dies bedeutet, dass Sie nur eine Compose-Funktion benötigen, die für jede Funktion funktioniert. Sie können jedoch mit C # -Funktionen gut genug arbeiten , wennFunc<rateKey, rateType>
Sie nur Closures verwenden . Der Punkt ist, dass Sie die Argumente, die die Zielfunktion nicht interessiert, nicht offenlegen.Func<rateType>
() => GetRate(rateKey)
Compose
Funktion ist wirklich nur dann nützlich, wenn Sie die Ausführung ausGetRate
irgendeinem Grund verzögern müssen , z. B. wenn SieCompose(FormatRate, GetRate)
an eine Funktion übergeben möchten, die eine von Ihnen festgelegte Rate bereitstellt, z. B. um sie auf jedes Element in a anzuwenden Liste.Es gibt absolut keinen Grund, eine Funktion und ihre Parameter zu übergeben, um sie dann mit diesen Parametern aufzurufen. Tatsächlich haben Sie in Ihrem Fall überhaupt keinen Grund, eine Funktion zu übergeben . Der Aufrufer kann auch einfach die Funktion selbst aufrufen und das Ergebnis übergeben.
Denken Sie darüber nach - anstatt Folgendes zu verwenden:
warum nicht einfach benutzen:
?
Es reduziert nicht nur unnötigen Code, sondern auch die Kopplung. Wenn Sie ändern möchten, wie die Rate abgerufen wird (z. B. wenn
getRate
jetzt zwei Argumente erforderlich sind), müssen Sie keine Änderungen vornehmenGetFormattedRate
.Ebenso gibt es keinen Grund zu schreiben,
GetFormattedRate(formatRate, getRate, rateKey)
anstatt zu schreibenformatRate(getRate(rateKey))
.Überkomplizieren Sie die Dinge nicht.
quelle
formatRate
, sollte dies möglicherweise auf die Raten abgebildet werden, die formatiert werden sollen).Wenn Sie eine Funktion unbedingt an die Funktion übergeben müssen, weil sie ein zusätzliches Argument übergibt oder sie in einer Schleife aufruft, können Sie stattdessen ein Lambda übergeben:
Das Lambda bindet die Argumente, von denen die Funktion nichts weiß, und verbirgt, dass sie überhaupt existieren.
quelle
Willst du das nicht?
Und dann nenne es so:
Wenn Sie eine Methode möchten, die sich in einer objektorientierten Sprache wie C # auf verschiedene Weise verhalten kann, müssen Sie die Methode normalerweise eine abstrakte Methode aufrufen. Wenn Sie keinen bestimmten Grund haben, es anders zu machen, sollten Sie es so machen.
Sieht das nach einer guten Lösung aus, oder gibt es Nachteile, an die Sie denken?
quelle
GetFormattedRate
Methode auch entfernen und einfach aufrufenIRateFormatter.FormatRate(rate)
). Das Grundkonzept ist jedoch korrekt, und ich denke, OP sollte seine Code-Polymorphie implementieren, wenn er mehrere Formatierungsmethoden benötigt.