Instanciando objetos nulos com o operador nulo-coalescente

12

Considere o seguinte cenário típico:

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

Estou imaginando o que é pensado na seguinte substituição usando o operador coalescente-nulo:

myObject = myObject ?? new myClass();

Não tenho certeza se devo usar o segundo formulário. Parece uma taquigrafia legal, mas a myObject = myObjectconstrução no começo parece que pode ser um pouco de cheiro de código.

Isso é uma coisa razoável a se fazer ou existe uma taquigrafia melhor que estou perdendo? Ou talvez: "São três linhas, supere isso!"?

Edit: Como já foi mencionado, talvez chamar isso de cenário típico seja um exagero. Normalmente, encontro essa situação ao recuperar uma entidade de um banco de dados que possui uma propriedade do tipo de referência filho que ainda pode ou não ser preenchida:

myClass myObject = myClassService.getById(id);
myObject.myChildObject = myObject.myChildObject ?? new myChildClass();
grin0048
fonte
15
Os iniciantes acreditam que coisas como essa são "hack", os desenvolvedores mais experientes chamam de "idiomática".
Doc Brown
Se o objeto se parecer com um objeto de valor ("possui semântica de valor", em fala idiomática), MyClassdeve fornecer um public static readonlymembro de um Emptyvalor de seu tipo. Veja String.Emptyno MSDN .
Rwong 22/08/2013
5
Não existe um ??=operador?
aviv
1
@aviv Eu gostaria que houvesse!
Loren Pechtel 11/11

Respostas:

16

Eu uso o operador coalescente nulo o tempo todo. Eu gosto da concisão disso.

Acho que esse operador é de natureza semelhante ao operador ternário (A? B: C). É preciso um pouco de prática antes que a leitura seja uma segunda natureza, mas uma vez que você está acostumado a isso, sinto que a legibilidade melhora em relação às versões longhand.

Além disso, a situação que você descreve é ​​apenas um cenário em que o operador é útil. Também é útil substituir construções como esta:

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

ou

return value != null ? value : otherValue;

com

return value ?? otherValue;
17 de 26
fonte
Definitivamente, concordo em geral sobre o uso deste operador. Quando vejo referências a usá-lo, normalmente é algo parecido myObject = myObject2 ?? myObject3. O que estou pensando especificamente é usar o operador para definir um objeto para si mesmo, a menos que seja nulo.
grin0048
2
Penso que o mesmo raciocínio se aplica em ambos os casos. É uma maneira mais concisa de expressar a mesma lógica.
17 de 26
Lembro-me de olhar a IL para cada uma dessas três formas uma vez. O primeiro e o segundo eram idênticos, mas o terceiro foi otimizado de uma maneira que se poderia assumir nullcomo uma comparação.
Jesse C. Slicer
2

O que estou querendo saber especificamente é usar o operador para definir um objeto para si mesmo, a menos que seja nulo.

Ele ?? operatoré chamado de operador de coalescência nula e é usado para definir um valor padrão para tipos de valor anulável ou tipos de referência. Retorna o operando esquerdo se o operando não for nulo; caso contrário, ele retornará o operando certo.

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

Mais detalhes e amostra de código sobre o operador de coalescência nula - artigo do MSDN .

Se você verificar a more than nullablecondição, como alternativa, poderá usar o operador ternário.

Operador ternário

Você também pode olhar ?: Operator . É chamado de operador ternário ou condicional . O operador condicional (? :) retorna um dos dois valores, dependendo do valor de uma expressão booleana.

Um tipo anulável pode conter um valor ou pode ser indefinido. O ?? O operador define o valor padrão a ser retornado quando um tipo anulável é atribuído a um tipo não anulável. Se você tentar atribuir um tipo de valor anulável a um tipo de valor não anulável sem usar o ?? operador, você gerará um erro em tempo de compilação. Se você usar uma conversão e o tipo de valor anulável não estiver definido no momento, uma exceção InvalidOperationException será lançada.

Um exemplo de código do MSDN -?: Operator (Referência de C #) :

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

// ?: conditional operator.
classify = (input.HasValue) ? ((input < 0) ? "negative" : "positive") : "undefined";
Yusubov
fonte
Então, pegando o exemplo da pergunta e aplicando o operador condicional, teríamos algo parecido myObject = (myObject == null) ? new myClass() : myObject. Portanto, a menos que eu esteja perdendo um uso melhor do operador condicional, ele parece ter o mesmo "problema" de definir um objeto para si mesmo (se não for nulo) que o operador de coalescência nula - e é menos conciso.
grin0048
Se você estiver verificando mais de uma condição (Não nula e maior que zero) - o operador condicional fará isso. No entanto, apenas a verificação nula funcionaria? operador.
Yusubov 22/08/13
2

Não acho que esse cenário seja (ou pelo menos deveria ser) típico.

Se você deseja que o valor padrão de algum campo não seja null, defina-o no inicializador de campos:

myClass myObject = new myClass();

Ou, se a inicialização for mais complicada, defina-a no construtor.

Se você deseja criar myClassapenas quando realmente precisa (por exemplo, porque a criação demora muito), você pode usar Lazy<T>:

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

(Isso chama o construtor padrão. Se a inicialização for mais complicada, passe o lambda que é criado myClasspara o Lazy<T>construtor.)

Para acessar o valor, use myObject.Value, que chamará a inicialização se esta for a primeira vez que você está acessando myObject.Value.

svick
fonte
A criação lenta da IMO só é útil se você não precisar garantir o objeto resultante. Somente o momento em que uso a criação lenta é quando tenho um resultado derivado em dinheiro que redefino quando algo muda e sei que precisarei muito do mesmo resultado e o recálculo é caro. outro sábio simplesmente não querem se preocupar com a verificação de nulo
catraca aberração
@ratchetfreak Sim, é por isso que sugeri o inicializador de campo primeiro.
precisa
Existem outros casos também. O que estou vendo no momento: a primeira passagem define os casos de exceção. A segunda passagem define os padrões para todo o resto. ?? = cortaria a linha ao meio.
Loren Pechtel 11/11
1

Eu gosto especialmente de usar ??em conjunto com parâmetros de método opcionais. Limito a necessidade de sobrecargas e garanto que a coisa tenha um valor.

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