Istnieje wzorzec w klasach C # zilustrowany przez Dictionary.TryGetValue
i int.TryParse
: metodę, która zwraca wartość logiczną wskazującą powodzenie operacji i parametr wyjściowy zawierający rzeczywisty wynik; jeśli operacja się nie powiedzie, parametr wyjściowy ma wartość null.
Załóżmy, że używam odwołań, które nie są zerowalne w języku C # 8, i chcę napisać metodę TryParse dla mojej własnej klasy. Poprawny podpis brzmi:
public static bool TryParse(string s, out MyClass? result);
Ponieważ w fałszywym przypadku wynik jest zerowy, zmienna wyjściowa musi być oznaczona jako pusta.
Jednak wzorzec Try jest ogólnie używany w następujący sposób:
if (MyClass.TryParse(s, out var result))
{
// use result here
}
Ponieważ wprowadzam gałąź tylko wtedy, gdy operacja się powiedzie, wynik nigdy nie powinien być pusty w tej gałęzi. Ale ponieważ oznaczyłem to jako zerowalne, muszę teraz to sprawdzić lub użyć !
do zastąpienia:
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
}
To jest brzydkie i trochę nieergonomiczne.
Ze względu na typowy wzorzec użytkowania mam inną opcję: kłamstwo na temat typu wyniku:
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;
}
Teraz typowe użycie staje się przyjemniejsze:
if (MyClass.TryParse(s, out var result))
{
Console.WriteLine("Look: {0}", result.SomeProperty); // no warning
}
ale nietypowe użycie nie wyświetla ostrzeżenia:
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.
}
Teraz nie jestem pewien, w którą stronę się udać. Czy istnieje oficjalna rekomendacja zespołu języka C #? Czy jest już jakiś kod CoreFX przekonwertowany na odwołania, które nie mają wartości zerowej, które mogłyby mi pokazać, jak to zrobić? (Poszedłem szukać TryParse
metod. IPAddress
Jest klasą, która ma jedną, ale nie została przekonwertowana w gałęzi master corefx.)
Jak radzi Dictionary.TryGetValue
sobie z tym ogólny kod ? (Prawdopodobnie ze specjalnym MaybeNull
atrybutem z tego, co znalazłem.) Co się stanie, gdy utworzę instancję Dictionary
z wartością o wartości innej niż null?
MyClass?
) i wykonaj na nim przełącznik za pomocą acase MyClass myObj:
i (opcjonalnie)case null:
.Odpowiedzi:
Wzorzec bool / out-var nie działa dobrze z nullable typami referencyjnymi, jak opisano. Zamiast walczyć z kompilatorem, użyj tej funkcji, aby uprościć rzeczy. Dodaj ulepszone funkcje dopasowywania wzorów w C # 8 i możesz potraktować zerowalne odniesienie jako „typ biednego mężczyzny”:
W ten sposób unikniesz bałaganu przy
out
parametrach i nie będziesz musiał walczyć z kompilatorem o mieszanienull
z niedopuszczalnymi wartościami referencyjnymi.W tym momencie upadł „typ biednego człowieka”. Wyzwanie, przed którym stoisz, polega na tym, że podczas korzystania z typów zerowalnych (NRT) kompilator będzie traktowany
Foo<T>
jako nie dopuszczający wartości zerowych. Ale spróbuj go zmienićFoo<T?>
i będzie chciał, abyT
ograniczenia były ograniczone do klasy lub struktury, ponieważ typy wartości dopuszczających wartości zerowe są zupełnie inne niż punkt widzenia CLR. Istnieje wiele obejść tego problemu:default
(wraz z!
)out
parametrów, nawet jeśli Twój kod rejestruje się bez zer,Maybe<T>
typ jako wartość zwracana, która nigdy potemnull
i owija, żebool
iout T
doHasValue
iValue
właściwości lub coś w tym rodzaju,Ja osobiście preferuję używanie,
Maybe<T>
ale wsparcie dekonstrukcji, aby można było dopasować wzór jako krotkę jak w 4 powyżej.źródło
TryParse(someString) is {} myClass
- Ta składnia wymaga przyzwyczajenia się, ale podoba mi się ten pomysł.TryParse(someString) is var myClass
wygląda mi łatwiej.x is var y
zawsze będzie prawdziwy, bez względu nax
to , czy jest zerowy, czy nie.Jeśli przybywających na to trochę późno, jak ja, okazuje się, że zespół NET skierowana go przez kilka atrybutów parametrów jak
MaybeNullWhen(returnValue: true)
wSystem.Diagnostics.CodeAnalysis
przestrzeni, które można wykorzystać do wzoru try.Na przykład:
co oznacza, że zostaniesz wykrzyczany, jeśli nie sprawdzisz
true
Dalsze szczegóły:
źródło
Nie sądzę, żeby tu był konflikt.
twój sprzeciw wobec
jest
Jednak w rzeczywistości nic nie stoi na przeszkodzie przypisaniu wartości null do parametru out w funkcjach TryParse w starym stylu.
na przykład.
Ostrzeżenie podane programiście, gdy używają parametru out bez sprawdzania, jest prawidłowe. Powinieneś sprawdzać!
Będzie mnóstwo przypadków, w których będziesz zmuszony zwrócić typy zerowalne, w których główna gałąź kodu zwraca typ nie dopuszczający wartości zerowych. Ostrzeżenie jest tylko po to, aby pomóc ci je wyrazić. to znaczy.
Niepozwalający na kod sposób spowoduje wygenerowanie wyjątku tam, gdzie byłby zerowy. Niezależnie od tego, czy parsujesz, dostajesz czy pierwszy
źródło
FirstOrDefault
można to porównać, ponieważ nieważność wartości zwracanej jest głównym sygnałem. WTryParse
metodach parametr out nie jest pusty, jeśli zwracana wartość jest prawdą, jest częścią kontraktu metody.TryParse
metody. JeśliIPAddress.TryParse
kiedykolwiek zwrócił prawdę, ale nie przypisał wartości zerowej do parametru out, zgłosiłbym to jako błąd.