Há um padrão nas classes C # exemplificadas por Dictionary.TryGetValue
e int.TryParse
: um método que retorna um valor booleano indicando o sucesso de uma operação e um parâmetro out contendo o resultado real; se a operação falhar, o parâmetro out será definido como nulo.
Vamos supor que estou usando referências não anuláveis em C # 8 e quero escrever um método TryParse para minha própria classe. A assinatura correta é esta:
public static bool TryParse(string s, out MyClass? result);
Como o resultado é nulo no caso falso, a variável out deve ser marcada como anulável.
No entanto, o padrão Try é geralmente usado assim:
if (MyClass.TryParse(s, out var result))
{
// use result here
}
Como apenas insiro a ramificação quando a operação é bem-sucedida, o resultado nunca deve ser nulo nessa ramificação. Mas como o marquei como nulo, agora tenho que verificar isso ou usar !
para substituir:
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
}
Isso é feio e um pouco anti-econômico.
Por causa do padrão de uso típico, tenho outra opção: mentir sobre o tipo de resultado:
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;
}
Agora, o uso típico se torna melhor:
if (MyClass.TryParse(s, out var result))
{
Console.WriteLine("Look: {0}", result.SomeProperty); // no warning
}
mas o uso atípico não recebe o aviso que deveria:
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.
}
Agora não tenho certeza de qual caminho seguir aqui. Existe uma recomendação oficial da equipe de idiomas C #? Existe algum código CoreFX já convertido em referências não anuláveis que possa me mostrar como fazer isso? ( Procurei TryParse
métodos. IPAddress
É uma classe que possui uma, mas não foi convertida na ramificação principal do corefx.)
E como código genérico como Dictionary.TryGetValue
lida com isso? (Possivelmente com um MaybeNull
atributo especial do que encontrei.) O que acontece quando instanciamos um Dictionary
com um tipo de valor não nulo?
MyClass?
) e faça uma troca com aecase MyClass myObj:
(opcional)case null:
.Respostas:
O padrão bool / out-var não funciona bem com tipos de referência anuláveis, como você descreve. Portanto, em vez de combater o compilador, use o recurso para simplificar as coisas. Jogue os recursos aprimorados de correspondência de padrões do C # 8 e você poderá tratar uma referência nula como um "talvez tipo de homem pobre":
Dessa forma, você evita mexer nos
out
parâmetros e não precisa brigar com o compilador misturando-onull
com referências não anuláveis.Nesse ponto, esse "tipo de pobre homem talvez" cai. O desafio que você enfrentará é que, ao usar tipos de referência anuláveis (NRT), o compilador tratará
Foo<T>
como não anulável. Mas tente alterá-lo paraFoo<T?>
e ele desejará que asT
restrições a uma classe ou estrutura como tipos de valor anuláveis sejam algo muito diferente do ponto de vista do CLR. Há uma variedade de soluções alternativas para isso:default
(junto com!
) osout
parâmetros, mesmo que seu código não esteja nulo,Maybe<T>
tipo que o valor de retorno, que é depois nuncanull
e envolve quebool
eout T
emHasValue
eValue
propriedades ou algo assim,Pessoalmente, sou a favor do uso,
Maybe<T>
mas o suporte a uma desconstrução para que ele possa corresponder ao padrão como uma tupla, como em 4 acima.fonte
TryParse(someString) is {} myClass
- Essa sintaxe levará algum tempo para se acostumar, mas eu gosto da ideia.TryParse(someString) is var myClass
parece mais fácil para mim.x is var y
sempre será verdadeiro, sejax
nulo ou não.Se você chegar um pouco tarde, como eu, a equipe do .NET abordou isso através de vários atributos de parâmetro, como
MaybeNullWhen(returnValue: true)
noSystem.Diagnostics.CodeAnalysis
espaço que você pode usar para o padrão try.Por exemplo:
o que significa que você é gritado se não procurar um
true
Detalhes adicionais:
fonte
Eu não acho que há um conflito aqui.
sua objeção a
é
No entanto, de fato, nada impede a atribuição de null ao parâmetro out nas funções TryParse do estilo antigo.
por exemplo.
O aviso dado ao programador quando eles usam o parâmetro out sem verificação está correto. Você deveria estar verificando!
Haverá muitos casos em que você será forçado a retornar tipos anuláveis, onde a ramificação principal do código retornará um tipo não anulável. O aviso está aí para ajudá-lo a torná-los explícitos. ie
A maneira não anulável de codificá-lo lançará uma exceção em que teria havido um nulo. Seja sua análise, obtenção ou inicialização
fonte
FirstOrDefault
possa ser comparado, porque a nulidade de seu valor de retorno é o sinal principal. NosTryParse
métodos, o parâmetro out não sendo nulo se o valor de retorno for verdadeiro faz parte do contrato do método.TryParse
método. SeIPAddress.TryParse
alguma vez retornou verdadeiro, mas não atribuiu um valor não nulo ao seu parâmetro out, eu o reportaria como um bug.