Crear instancias de objetos nulos con un operador de fusión nula

12

Considere el siguiente escenario típico:

if(myObject == null) {
    myObject = new myClass();
}

Me pregunto qué se piensa del siguiente reemplazo usando el operador de fusión nula:

myObject = myObject ?? new myClass();

No estoy seguro de si debería estar usando la segunda forma. Parece una buena taquigrafía, pero la myObject = myObjectconstrucción al principio parece que podría ser un poco de olor a código.

¿Es esto algo razonable de hacer, o hay una mejor taquigrafía que me estoy perdiendo? O tal vez, "¡Son tres líneas, supéralo!"

Editar: Como se ha mencionado, quizás llamar a esto un escenario típico es una exageración. Por lo general, encuentro que me encuentro con esta situación cuando estoy recuperando una entidad de una base de datos que tiene una propiedad de tipo de referencia secundaria que puede o no estar poblada todavía:

myClass myObject = myClassService.getById(id);
myObject.myChildObject = myObject.myChildObject ?? new myChildClass();
grin0048
fuente
15
Los novatos creen que cosas como esta son "hackish", los desarrolladores más experimentados simplemente lo llaman "idiomático".
Doc Brown
Si el objeto se parece a un objeto de valor ("tiene semántica de valor", en lenguaje idiomático), MyClassdebe proporcionar un public static readonlymiembro de un Emptyvalor de su tipo. Ver String.Emptyen MSDN .
rwong
55
¿No hay un ??=operador?
aviv
1
@aviv ¡Desearía que hubiera!
Loren Pechtel

Respuestas:

16

Yo uso el operador de fusión nula todo el tiempo. Me gusta su concisión.

Encuentro que este operador es similar en naturaleza al operador ternario (A? B: C). Se necesita un poco de práctica antes de leerlo, es una segunda naturaleza, pero una vez que está acostumbrado, siento que la legibilidad mejora con respecto a las versiones manuales.

Además, la situación que describe es solo un escenario en el que el operador es útil. También es útil para reemplazar construcciones como esta:

if (value != null)
{
    return value;
}
else
{ 
    return otherValue;
}

o

return value != null ? value : otherValue;

con

return value ?? otherValue;
17 de 26
fuente
Definitivamente de acuerdo en general sobre el uso de este operador. Sin embargo, cuando veo referencias para usarlo, generalmente es algo así myObject = myObject2 ?? myObject3. Lo que me pregunto específicamente es usar el operador para establecer un objeto en sí mismo a menos que sea nulo.
grin0048
2
Creo que el mismo razonamiento se aplica en cualquier caso. Es una forma más concisa de expresar la misma lógica.
17 de 26
Recuerdo haber visto el IL para cada una de esas tres formas una vez. El primero y el segundo eran idénticos, pero el tercero estaba optimizado de una manera que podría suponer que nullera una comparación.
Jesse C. Slicer
2

Lo que me pregunto específicamente es usar el operador para establecer un objeto en sí mismo a menos que sea nulo.

El ?? operatorse llama operador de fusión nula y se utiliza para definir un valor predeterminado para los tipos de valores anulables o tipos de referencia. Devuelve el operando de la izquierda si el operando no es nulo; de lo contrario, devuelve el operando correcto.

myObject = myObject ?? new myObject(); - instantiate default value of object

Más detalles y muestra de código sobre el operador de fusión nula - Artículo de MSDN .

Si verifica la more than nullablecondición, como alternativa , puede utilizar el operador ternario.

Operador ternario

También puede mirar ?: Operador . Se llama operador ternario u condicional . El operador condicional (? :) devuelve uno de los dos valores según el valor de una expresión booleana.

Un tipo anulable puede contener un valor o puede ser indefinido. Los ?? El operador define el valor predeterminado que se devolverá cuando se asigne un tipo anulable a un tipo no anulable. Si intenta asignar un tipo de valor anulable a un tipo de valor no anulable sin utilizar ?? operador, generará un error en tiempo de compilación. Si usa una conversión y el tipo de valor anulable no está definido actualmente, se generará una excepción InvalidOperationException.

Un ejemplo de código de MSDN -?: Operador (Referencia de C #) :

int? input = Convert.ToInt32(Console.ReadLine());
string classify;

// ?: conditional operator.
classify = (input.HasValue) ? ((input < 0) ? "negative" : "positive") : "undefined";
Yusubov
fuente
Entonces, tomando el ejemplo de la pregunta y aplicando el operador condicional, tendríamos algo así myObject = (myObject == null) ? new myClass() : myObject. Entonces, a menos que me falte un mejor uso del operador condicional, parece tener el mismo "problema" al establecer un objeto en sí mismo (si no es nulo) que el operador de fusión nula, y es menos conciso.
grin0048
Si está buscando más de una condición (no nula y mayor que cero), entonces el operador condicional lo hará. Sin embargo, ¿solo la comprobación nula funcionaría? operador.
Yusubov
2

No creo que ese escenario sea (o al menos debería ser) típico.

Si desea que el valor predeterminado de algún campo no sea null, configúrelo en el inicializador de campo:

myClass myObject = new myClass();

O, si la inicialización es más complicada, configúrela en el constructor.

Si desea crear myClasssolo cuando realmente lo necesita (por ejemplo, porque crearlo lleva mucho tiempo), puede usar Lazy<T>:

Lazy<myClass> myObject = new Lazy<myClass>();

(Esto llama al constructor predeterminado. Si la inicialización es más complicada, pase lambda que crea myClassal Lazy<T>constructor).

Para acceder al valor, use myObject.Value, que llamará a la inicialización si es la primera vez que accede myObject.Value.

svick
fuente
La creación diferida de IMO solo es útil si no se garantiza que necesitará el objeto resultante, solo cuando uso la creación diferida es cuando tengo un resultado derivado en efectivo que restablezco cuando algo cambia y sé que necesitaré mucho el mismo resultado Y el recalc es caro. otro sabio sólo que no quiero que molestarse con el cheque nulo
trinquete monstruo
@ratchetfreak Sí, por eso sugerí primero el inicializador de campo.
svick
Hay otros casos también. Lo que estoy viendo en este momento: el primer pase establece los casos de excepción. El segundo pase establece valores predeterminados para todo lo demás. ?? = cortaría la línea por la mitad.
Loren Pechtel
1

Especialmente me gusta usar ??junto con parámetros de métodos opcionales. Limito la necesidad de sobrecargas y me aseguro de que la cosa tenga un valor.

public void DoSomething (MyClass thing1 = null) {
    thing1 = thing1 ?? new MyClass();
}
radarbob
fuente