?. Operadores condicionais nulos ?. e ?() (Visual Basic)

Testa o valor do operando esquerdo para nulo (Nothing)antes de executar um acesso de membro (?.) ou uma operação de índice (?()); retorna Nothing se o operando esquerdo é avaliado como Nothing. Observe que, em expressões que normalmente retornam tipos de valor, o operador nulo condicional retorna um Nullable<T>.

Esses operadores ajudam a escrever menos código para lidar com verificações de nulidade, especialmente ao entrar em estruturas de dados. Por exemplo:

' Nothing if customers is Nothing
Dim length As Integer? = customers?.Length

' Nothing if customers is Nothing
Dim first As Customer = customers?(0)

' Nothing if customers, the first customer, or Orders is Nothing
Dim count As Integer? = customers?(0)?.Orders?.Count()

Para comparação, o código alternativo para a primeira dessas expressões sem um operador condicional nulo é:

Dim length As Integer?
If customers IsNot Nothing Then
   length = customers.Length
Else
    length = Nothing
End If

Às vezes, você precisa executar uma ação em um objeto que pode ser nulo, com base no valor de um membro booliano nesse objeto (como a propriedade booliana IsAllowedFreeShipping no seguinte exemplo):

Dim customer = FindCustomerByID(123) 'customer will be Nothing if not found.

If customer IsNot Nothing AndAlso customer.IsAllowedFreeShipping Then
  ApplyFreeShippingToOrders(customer)
End If

Você pode reduzir o código e evitar a verificação manual de nulo usando o operador nulo condicional da seguinte maneira:

Dim customer = FindCustomerByID(123) 'customer will be Nothing if not found.

If customer?.IsAllowedFreeShipping Then ApplyFreeShippingToOrders(customer)

Os operadores condicionais nulos estão entrando em curto-circuito. Se uma operação em uma cadeia de operações de índice e acesso de membro condicionais retornar Nothing, o restante da execução da cadeia será interrompido. No exemplo a seguir, C(E) não será executado se A, B ou C for avaliado como Nothing.

A?.B?.C?(E)

Observe que se Not someStr?.Contains("some string") ou qualquer outro valor que seja avaliado como Boolean? tiver o valor de nothing ou HasValue=false, o bloco else será executado. A avaliação segue a avaliação do SQL em que nulo/nada não é igual a nada, nem mesmo outro nulo/nada.

Outro uso para o acesso de membro condicional nulo é invocar delegados de maneira thread-safe com muito menos código. O exemplo a seguir define dois tipos, NewsBroadcaster e NewsReceiver. Itens de notícias são enviados ao receptor pelo delegado NewsBroadcaster.SendNews.

Public Module NewsBroadcaster
   Dim SendNews As Action(Of String)

   Public Sub Main()
      Dim rec As New NewsReceiver()
      Dim rec2 As New NewsReceiver()
      SendNews?.Invoke("Just in: A newsworthy item...")
   End Sub

   Public Sub Register(client As Action(Of String))
      SendNews = SendNews.Combine({SendNews, client})
   End Sub
End Module

Public Class NewsReceiver
   Public Sub New()
      NewsBroadcaster.Register(AddressOf Me.DisplayNews)
   End Sub

   Public Sub DisplayNews(newsItem As String)
      Console.WriteLine(newsItem)
   End Sub
End Class

Se não houver elementos na lista de invocação SendNews, o delegado SendNews gerará NullReferenceException. Antes dos operadores condicionais nulos, um código como o seguinte garantiu que a lista de invocação do delegado não fosse Nothing:

SendNews = SendNews.Combine({SendNews, client})
If SendNews IsNot Nothing Then
   SendNews("Just in...")
End If

A nova maneira é muito mais simples:

SendNews = SendNews.Combine({SendNews, client})
SendNews?.Invoke("Just in...")

A nova forma é thread-safe porque o compilador gera código para avaliar SendNews somente uma vez, mantendo o resultado em uma variável temporária. Você precisa chamar explicitamente o método Invoke porque não há nenhuma sintaxe de invocação de delegado condicional nulo SendNews?(String).

Confira também