Ich schreibe eine Art Warteschlangenimplementierung mit einer TryDequeue
Methode, die ein ähnliches Muster wie verschiedene .NET- TryParse
Methoden verwendet. Wenn die Aktion erfolgreich war, out
gebe ich einen booleschen Wert zurück und verwende einen Parameter, um den tatsächlichen Wert in der Warteschlange zurückzugeben.
public bool TryDequeue(out Message message) => _innerQueue.TryDequeue(out message);
Jetzt vermeide ich gerne out
Params, wann immer ich kann. C # 7 gibt uns variable Delkarationen aus, um die Arbeit mit ihnen zu erleichtern, aber ich betrachte Params immer noch eher als notwendiges Übel als als nützliches Werkzeug.
Das von dieser Methode gewünschte Verhalten ist wie folgt:
- Wenn es ein zu löschendes Objekt gibt, geben Sie es zurück.
- Wenn keine zu löschenden Elemente vorhanden sind (die Warteschlange ist leer), stellen Sie dem Anrufer genügend Informationen zur Verfügung, um entsprechend zu handeln.
- Geben Sie nicht einfach einen leeren Artikel zurück, wenn noch keine Artikel verfügbar sind.
- Machen Sie keine Ausnahmen, wenn Sie versuchen, eine leere Warteschlange zu löschen.
Derzeit verwendet ein Aufrufer dieser Methode fast immer ein Muster wie das folgende (unter Verwendung der C # 7-Out-Variablensyntax):
if (myMessageQueue.TryDequeue(out Message dequeued))
MyMessagingClass.SendMessage(dequeued)
else
Console.WriteLine("No messages!"); // do other stuff
Welches ist nicht das schlechteste, alles in allem. Aber ich kann nicht anders, als zu glauben, dass es dafür vielleicht bessere Möglichkeiten gibt (ich bin absolut bereit zuzugeben, dass es keine gibt). Ich hasse es, wenn der Anrufer seinen Fluss mit einer Bedingung unterbrechen muss, wenn er nur einen Wert erhalten möchte, falls einer existiert.
Welche anderen Muster gibt es, um dasselbe "Versuch" -Verhalten zu erreichen?
Für den Kontext kann diese Methode möglicherweise in VB-Projekten aufgerufen werden. Bonuspunkte für etwas, das in beiden Fällen gut funktioniert. Diese Tatsache sollte jedoch sehr wenig Gewicht haben.
Option<T>
Struktur und geben Sie sie zurück. Diesebool Try(..., out data)
Funktionen sind ein Gräuel.Antworten:
Verwenden Sie einen Optionstyp, dh ein Objekt eines Typs mit zwei Versionen, der normalerweise entweder "Some" (wenn ein Wert vorhanden ist) oder "None" (wenn kein Wert vorhanden ist) genannt wird ... oder gelegentlich Sie heißen Just and Nothing. Es gibt dann Funktionen in diesen Typen, mit denen Sie auf den Wert zugreifen können, wenn er vorhanden ist, auf Anwesenheit testen und vor allem auf eine Methode, mit der Sie eine Funktion anwenden können, die eine weitere Option auf den Wert zurückgibt, wenn er vorhanden ist (normalerweise in C #) Sie müssen FlatMap heißen, obwohl es in anderen Sprachen häufig als Bind bezeichnet wird. Der Name ist in C # von entscheidender Bedeutung, da Sie mit der Methode dieses Namens und Typs Ihre Option-Objekte in LINQ-Anweisungen verwenden können.
Zusätzliche Funktionen können Methoden wie IfPresent und IfNotPresent zum Aufrufen von Aktionen unter den relevanten Bedingungen und OrElse (das einen Standardwert ersetzt, wenn kein Wert vorhanden ist, andernfalls jedoch ein no op) usw. sein.
Ihr Beispiel könnte dann ungefähr so aussehen:
Dies ist das Monadenmuster Option (oder Vielleicht) und es ist äußerst nützlich. Es gibt bereits Implementierungen (z. B. https://github.com/nlkl/Optional/blob/master/README.md ), aber es ist auch nicht schwer, diese selbst zu erstellen.
(Möglicherweise möchten Sie dieses Muster so erweitern, dass Sie stattdessen eine Beschreibung der Fehlerursache zurückgeben, wenn die Methode fehlschlägt. Dies ist vollständig erreichbar und wird häufig als Either-Monade bezeichnet. Wie der Name schon sagt, können Sie dieselbe FlatMap verwenden Muster, um auch in diesem Fall die Arbeit zu erleichtern)
quelle
Option
/Maybe
ist, dass es sich um eine Monade handelt (wenn sie natürlich als solche implementiert ist), was bedeutet, dass sie sicher verkettet, eingewickelt, ausgepackt und verarbeitet werden kann, ohne die verschiedenen Fälle jemals direkt behandeln zu müssen, und diese Verkettung und Die Verarbeitung kann mit LINQ Query Expressions erfolgen.flatMap
/bind
wird aufgerufen ,SelectMany
in .NET.Try...
Namenskonventionen in .NET verletzen (und möglicherweise Betreuer verwirren).Die gegebenen Antworten sind gut und ich würde mit einem von ihnen gehen. Betrachten Sie diese Antwort nur als Ergänzung einiger alternativer Ideen:
Machen Sie Unterklassen von
Message
aufgerufenenSomeMessage
undNoMessage
.Dequeue
kann jetzt zurückkehren,NoMessage
wenn keine Nachricht vorhanden ist undSomeMessage
wenn eine Nachricht vorhanden ist. Wenn der Angerufene herausfinden möchte, in welchem Fall er sich befindet, kann er dies leicht durch Typprüfung tun. Wenn sie es nicht tun, machen sieNoMessage
einfach nichts, wenn eine ihrer Methoden aufgerufen wird, und hey, sie bekommen, was sie verlangt haben.Das gleiche wie oben, aber
Message
implizit konvertierbar machenbool
(oder implementierenoperator true / operator false
). Jetzt können Sie sagenif (message)
und haben es wahr, wenn die Nachricht gut und falsch ist, wenn es schlecht ist. (Dies ist mit ziemlicher Sicherheit eine schlechte Idee, aber ich füge sie der Vollständigkeit halber hinzu!)C # 7 hat Tupel. Gib ein
(bool, Message)
Tupel zurück.Machen Sie
Message
einen Strukturtyp und kehren Sie zurückMessage?
.quelle
In C # 7 können Sie Pattern Matching verwenden, um dies auf elegantere Weise zu erreichen:
In diesem speziellen Fall würde ich wahrscheinlich Ereignisse verwenden, anstatt die Warteschlange wiederholt abzufragen.
quelle
TryDequeue
einige Marker-Interfaces mit einerMessage
Implementierung und einige "nichts" -Implementierungen zurückgegeben werden? Sicher, es ist eleganter auf der Call-Site, aber Sie müssen 3 Implementierungen im tatsächlichen Code implementieren, was selbst unmöglich sein kann, wennMessage
es sich um einen vorhandenen Typ handelt.An einer Try ... -Methode (mit einem out-Parameter) ist nichts auszusetzen, wenn ein Fehler (kein Wert) genauso häufig auftritt wie ein Erfolg.
Wenn Sie jedoch darauf bestehen, die Dinge anders zu machen, möchten Sie möglicherweise eine leere Nachricht zurückgeben und diese entweder nur senden (nicht mehr Ihr Problem) oder die if-Anweisung verschieben, bis Sie wirklich wissen müssen, ob Sie eine Nachricht mit Inhalt haben oder nicht.
Beachten Sie, dass jemand den Test sowieso irgendwann durchführen muss. Ich würde sagen, der Test sollte durchgeführt werden, wann und wo die Frage aktuell ist. Dies ist zum Zeitpunkt der Dequeing.
quelle