Hay un patrón en las clases de C # ejemplificado por Dictionary.TryGetValue
y int.TryParse
: un método que devuelve un valor booleano que indica el éxito de una operación y un parámetro de salida que contiene el resultado real; Si la operación falla, el parámetro de salida se establece en nulo.
Supongamos que estoy usando referencias no nulables de C # 8 y quiero escribir un método TryParse para mi propia clase. La firma correcta es esta:
public static bool TryParse(string s, out MyClass? result);
Como el resultado es nulo en el caso falso, la variable de salida debe marcarse como nulable.
Sin embargo, el patrón Try generalmente se usa así:
if (MyClass.TryParse(s, out var result))
{
// use result here
}
Como solo entro en la rama cuando la operación tiene éxito, el resultado nunca debe ser nulo en esa rama. Pero debido a que lo marqué como anulable, ahora tengo que verificar eso o usar !
para anular:
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
}
Esto es feo y un poco poco ergonómico.
Debido al patrón de uso típico, tengo otra opción: mentir sobre el 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;
}
Ahora el uso típico se vuelve más agradable:
if (MyClass.TryParse(s, out var result))
{
Console.WriteLine("Look: {0}", result.SomeProperty); // no warning
}
pero el uso atípico no recibe la advertencia que debería:
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.
}
Ahora no estoy seguro de qué camino tomar aquí. ¿Hay alguna recomendación oficial del equipo de lenguaje C #? ¿Hay algún código CoreFX ya convertido en referencias no anulables que pueda mostrarme cómo hacer esto? (Fui a buscar TryParse
métodos. IPAddress
Es una clase que tiene uno, pero no se ha convertido en la rama maestra de corefx).
¿Y cómo se ocupa esto de un código genérico Dictionary.TryGetValue
? (Posiblemente con un MaybeNull
atributo especial de lo que encontré.) ¿Qué sucede cuando instancia una Dictionary
con un tipo de valor no anulable?
MyClass?
), y hacer un cambio con uncase MyClass myObj:
y (una opción todos)case null:
.Respuestas:
El patrón bool / out-var no funciona bien con los tipos de referencia anulables, como usted describe. Entonces, en lugar de luchar contra el compilador, use la función para simplificar las cosas. Agregue las características mejoradas de coincidencia de patrones de C # 8 y puede tratar una referencia anulable como "tipo de hombre pobre":
De esa manera, evita jugar con los
out
parámetros y no tiene que luchar con el compilador por mezclarnull
con referencias no anulables.En este punto, ese "tipo de hombre pobre quizás" se cae. El desafío que enfrentará es que cuando use tipos de referencia anulables (NRT), el compilador lo tratará
Foo<T>
como no anulable. Pero intente cambiarloFoo<T?>
y querráT
restringirlo a una clase o estructura ya que los tipos de valores anulables son algo muy diferente desde el punto de vista del CLR. Hay una variedad de soluciones para esto:default
(junto con!
) para losout
parámetros, aunque su código se registre sin nulosMaybe<T>
tipo que el valor de retorno, que luego nuncanull
y se envuelve de quebool
yout T
enHasValue
yValue
propiedades o algo así,Personalmente, estoy a favor de usarlo,
Maybe<T>
pero que sea compatible con una deconstrucción para que pueda coincidir con el patrón como una tupla como en 4, arriba.fuente
TryParse(someString) is {} myClass
- Esta sintaxis llevará un tiempo acostumbrarse, pero me gusta la idea.TryParse(someString) is var myClass
me parece más fácilx is var y
lo que siempre será cierto, seax
nulo o no.Si llega a esto un poco tarde, como yo, resulta que el equipo de .NET lo abordó a través de un conjunto de atributos de parámetros como
MaybeNullWhen(returnValue: true)
en elSystem.Diagnostics.CodeAnalysis
espacio que puede usar para el patrón de prueba.Por ejemplo:
lo que significa que te gritan si no compruebas si hay un
true
Más detalles:
fuente
No creo que haya un conflicto aquí.
tu objeción a
es
Sin embargo, de hecho, no hay nada que impida la asignación de nulo al parámetro out en las funciones TryParse de estilo antiguo.
p.ej.
La advertencia dada al programador cuando utiliza el parámetro de salida sin verificar es correcta. ¡Deberías estar comprobando!
Habrá un montón de casos en los que se verá obligado a devolver tipos anulables donde la rama principal del código devuelve un tipo no anulable. La advertencia está ahí para ayudarlo a hacer esto explícito. es decir.
La forma no anulable de codificarlo arrojará una excepción donde hubiera habido un valor nulo. Ya sea que esté analizando, obteniendo o iniciando
fuente
FirstOrDefault
se pueda comparar, porque la nulidad de su valor de retorno es la señal principal. En losTryParse
métodos, el parámetro out no es nulo si el valor de retorno es verdadero es parte del contrato del método.TryParse
método. SiIPAddress.TryParse
alguna vez devuelve verdadero pero no asignó no nulo a su parámetro out, lo reportaría como un error.