Существует закономерность в классах C # на примере Dictionary.TryGetValue
и int.TryParse
: метод , который возвращает логическое значение , указывающее успех операции и параметра из содержащего фактический результат; если операция не удалась, для параметра out устанавливается значение null.
Давайте предположим, что я использую ненулевые ссылки C # 8 и хочу написать метод TryParse для моего собственного класса. Правильная подпись заключается в следующем:
public static bool TryParse(string s, out MyClass? result);
Поскольку в ложном случае результат равен нулю, переменная out должна быть помечена как nullable.
Тем не менее, шаблон Try обычно используется так:
if (MyClass.TryParse(s, out var result))
{
// use result here
}
Поскольку я вхожу только в ветвь, когда операция завершается успешно, результат в этой ветке никогда не должен быть нулевым. Но поскольку я пометил его как nullable, теперь я должен либо проверить это, либо использовать !
для переопределения:
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
}
Это некрасиво и немного неэргономично.
Из-за типичного шаблона использования у меня есть еще один вариант: ложь о типе результата:
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;
}
Теперь типичное использование становится лучше:
if (MyClass.TryParse(s, out var result))
{
Console.WriteLine("Look: {0}", result.SomeProperty); // no warning
}
но нетипичное использование не получает предупреждение, оно должно:
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.
}
Теперь я не уверен, куда идти. Есть ли официальная рекомендация от языковой команды C #? Есть ли какой-либо код CoreFX, уже преобразованный в необнуляемые ссылки, который может показать мне, как это сделать? (Я искал TryParse
методы. IPAddress
Это класс, в котором он есть, но он не был преобразован в основную ветку corefx.)
И как Dictionary.TryGetValue
с этим справляется общий код ? (Возможно, со специальным MaybeNull
атрибутом из того, что я нашел.) Что произойдет, когда я создаю экземпляр Dictionary
с ненулевым типом значения?
MyClass?
), и переключиться на него с помощьюcase MyClass myObj:
и (опция все)case null:
.Ответы:
Как вы описываете, шаблон bool / out-var не очень хорошо работает со ссылочными типами, допускающими обнуляемость. Поэтому вместо того, чтобы бороться с компилятором, используйте эту функцию, чтобы упростить вещи. Добавьте в C # 8 улучшенные возможности сопоставления с образцом, и вы можете рассматривать нулевую ссылку как «тип бедняка»:
Таким образом, вы избегаете возиться с
out
параметрами и не должны бороться с компилятором за смешиваниеnull
с необнуляемыми ссылками.В этот момент этот «тип бедного человека» падает. Проблема, с которой вы столкнетесь, заключается в том, что при использовании ссылочных типов, допускающих обнуляемость (NRT), компилятор будет обрабатываться
Foo<T>
как ненулевые. Но попробуйте изменить его на,Foo<T?>
и он захочет, чтобы этоT
ограничение класса или структуры, поскольку типы значений, допускающие значение NULL, с точки зрения CLR - это совсем другое. Есть множество способов обойти это:default
(вместе с!
) дляout
параметров, даже если ваш код не имеет нулевых значений,Maybe<T>
тип в качестве возвращаемого значения, которое тогда никогда не будетnull
и оборачивает этоbool
иout T
вHasValue
иValue
свойства или некоторые такие,Лично я одобряю использование,
Maybe<T>
но поддерживаю деконструкцию, чтобы можно было сопоставить шаблон как кортеж, как в 4, выше.источник
TryParse(someString) is {} myClass
- К этому синтаксису потребуется некоторое привыкание, но мне нравится идея.TryParse(someString) is var myClass
выглядит проще для меня.x is var y
всегда будет верным, независимо от того,x
является ли он нулевым или нет.Если вы приходите к этому немного поздно, как я, оказывается, что команда .NET обратилась к нему с помощью набора атрибутов параметров, как
MaybeNullWhen(returnValue: true)
вSystem.Diagnostics.CodeAnalysis
пространстве, которое вы можете использовать для шаблона try.Например:
а это значит, что вас кричат, если вы не проверяете
true
Более подробная информация:
источник
Я не думаю, что здесь есть конфликт.
Ваше возражение против
является
Однако на самом деле ничто не мешает присвоить значение null параметру out в функциях TryParse старого стиля.
например.
Предупреждение, выдаваемое программисту, когда он использует параметр out без проверки, является правильным. Вы должны проверять!
Будет множество случаев, когда вы будете вынуждены возвращать обнуляемые типы, где основная ветвь кода возвращает ненулевой тип. Предупреждение только для того, чтобы помочь вам сделать это явным. то есть.
Не допускающий обнуления способ его кодирования вызовет исключение, в котором он был бы нулевым. Будь то разбор, получение или первый
источник
FirstOrDefault
можно сравнивать, потому что нулевой его возвращаемое значение является основным сигналом. ВTryParse
методах параметр out, не равный нулю, если возвращаемое значение равно true, является частью контракта метода.TryParse
метода. ЕслиIPAddress.TryParse
когда-либо возвращать значение true, но не назначать ненулевое значение для его параметра out, я сообщу об этом как об ошибке.