Es ist ein Muster , in C # Klassen veranschaulicht durch , Dictionary.TryGetValue
und int.TryParse
: ein Verfahren , das gibt einen booleschen angibt Erfolg einer Operation und einem Out - Parameter das tatsächliche Ergebnis enthält; Wenn der Vorgang fehlschlägt, wird der Parameter out auf null gesetzt.
Angenommen, ich verwende C # 8-Referenzen, die nicht nullwertfähig sind, und möchte eine TryParse-Methode für meine eigene Klasse schreiben. Die korrekte Signatur lautet:
public static bool TryParse(string s, out MyClass? result);
Da das Ergebnis im falschen Fall null ist, muss die Variable out als nullbar markiert werden.
Das Try-Muster wird jedoch im Allgemeinen folgendermaßen verwendet:
if (MyClass.TryParse(s, out var result))
{
// use result here
}
Da ich den Zweig nur betrete, wenn der Vorgang erfolgreich ist, sollte das Ergebnis in diesem Zweig niemals null sein. Aber da ich es als nullbar markiert habe, muss ich es jetzt entweder überprüfen oder !
zum Überschreiben verwenden:
if (MyClass.TryParse(s, out var result))
{
Console.WriteLine("Look: {0}", result.SomeProperty); // compiler warning, could be null
Console.WriteLine("Look: {0}", result!.SomeProperty); // need override
}
Das ist hässlich und ein bisschen unergonomisch.
Aufgrund des typischen Verwendungsmusters habe ich eine andere Option: Lüge über den Ergebnistyp:
public static bool TryParse(string s, out MyClass result) // not nullable
{
// Happy path sets result to non-null and returns true.
// Error path does this:
result = null!; // override compiler complaint
return false;
}
Jetzt wird die typische Verwendung schöner:
if (MyClass.TryParse(s, out var result))
{
Console.WriteLine("Look: {0}", result.SomeProperty); // no warning
}
atypische Nutzung wird jedoch nicht entsprechend gewarnt:
else
{
Console.WriteLine("Fail: {0}", result.SomeProperty);
// Yes, result is in scope here. No, it will never be non-null.
// Yes, it will throw. No, the compiler won't warn about it.
}
Jetzt bin ich mir nicht sicher, welchen Weg ich hier einschlagen soll. Gibt es eine offizielle Empfehlung des C # -Sprachteams? Gibt es CoreFX-Code, der bereits in nicht nullfähige Verweise konvertiert wurde, die mir zeigen könnten, wie das geht? (Ich habe nach TryParse
Methoden IPAddress
gesucht. Ist eine Klasse, die eine hat, aber sie wurde im Master-Zweig von corefx nicht konvertiert.)
Und wie geht generischer Code damit Dictionary.TryGetValue
um? (Möglicherweise mit einem speziellen MaybeNull
Attribut aus dem, was ich gefunden habe.) Was passiert, wenn ich einen Dictionary
mit einem nicht nullwertfähigen Werttyp instanziiere ?
MyClass?
), und schalten Sie es mit einemcase MyClass myObj:
und (optional) eincase null:
.Antworten:
Das bool / out-var-Muster funktioniert nicht gut mit nullfähigen Referenztypen, wie Sie beschreiben. Verwenden Sie die Funktion, um die Dinge zu vereinfachen, anstatt den Compiler zu bekämpfen. Wenn Sie die verbesserten Mustervergleichsfunktionen von C # 8 verwenden, können Sie eine nullfähige Referenz als "armen Mannes vielleicht Typ" behandeln:
Auf diese Weise vermeiden Sie das Herumspielen von
out
Parametern und müssen sich nicht mit dem Compilernull
herumschlagen, wenn es um das Mischen mit nicht nullwertfähigen Referenzen geht.Zu diesem Zeitpunkt fällt dieser "arme Mann ist vielleicht Typ". Die Herausforderung besteht darin, dass der Compiler bei Verwendung von NRT-Referenztypen (nullable reference types) diese
Foo<T>
als nicht nullable behandelt. Aber versuchen Sie es zu ändernFoo<T?>
und es wird gewünscht, dass dieT
Einschränkung auf eine Klasse oder Struktur, da nullbare Werttypen eine ganz andere Sache aus Sicht der CLR sind. Hierfür gibt es eine Vielzahl von Umgehungsmöglichkeiten:default
(zusammen mit!
) fürout
Parameter, obwohl sich Ihr Code ohne Nullen anmeldet.Maybe<T>
Typ als Rückgabewert, der dann never istnull
und dasbool
undout T
inHasValue
undValue
properties oder so einschließt,Ich persönlich bevorzuge
Maybe<T>
es, ein Dekonstrukt zu verwenden, aber zu haben, damit es als Tupel wie in 4 oben musterangepasst werden kann.quelle
TryParse(someString) is {} myClass
- Diese Syntax ist gewöhnungsbedürftig, aber die Idee gefällt mir.TryParse(someString) is var myClass
sieht für mich einfacher aus.x is var y
ist immer wahr, unabhängig davon, obx
es null ist oder nicht.Wenn Sie, wie ich, etwas spät dazu kommen, hat sich herausgestellt, dass das .NET-Team dies durch eine Reihe von Parameterattributen wie
MaybeNullWhen(returnValue: true)
in demSystem.Diagnostics.CodeAnalysis
Bereich, den Sie für das Testmuster verwenden können, angesprochen hat .Beispielsweise:
was bedeutet, dass Sie angeschrien werden, wenn Sie nicht nach einem suchen
true
Weitere Details:
quelle
Ich glaube nicht, dass es hier einen Konflikt gibt.
Ihr Einwand gegen
ist
Tatsächlich steht der Zuweisung von null zum out-Parameter in den TryParse-Funktionen des alten Stils jedoch nichts entgegen.
z.B.
Die Warnung, die dem Programmierer gegeben wird, wenn er den out-Parameter ohne Überprüfung verwendet, ist korrekt. Sie sollten überprüfen!
Es wird viele Fälle geben, in denen Sie gezwungen sind, nullfähige Typen zurückzugeben, während der Hauptzweig des Codes einen nicht nullfähigen Typ zurückgibt. Die Warnung soll Ihnen nur dabei helfen, diese explizit zu machen. dh
Die nicht nullfähige Codierung löst eine Ausnahme aus, bei der eine Null aufgetreten wäre. Egal ob Sie analysieren, lesen oder anfangen
quelle
FirstOrDefault
verglichen werden, da die nullness seines Rückgabewertes ist das Hauptsignal. In denTryParse
Methoden ist der Parameter out, der nicht null ist, wenn der Rückgabewert true ist, Teil des Methodenvertrags.TryParse
Methode erwarte . WennIPAddress.TryParse
jemals true zurückgegeben wurde, dem out-Parameter jedoch kein Wert ungleich null zugewiesen wurde, würde ich dies als Fehler melden.