Problemas de tipos anuláveis ​​do C # 9

Sep 12 2020

Considere o seguinte código (visualização do VS 16.8.0 Preview 2.1 C # 9.0):

#nullable enable

using System.Collections.Generic;

class Archive<T> where T : notnull 
{
  readonly Dictionary<string, T> Dict = new();

  public T? GetAt(string key) 
  { 
    return Dict.TryGetValue(key, out var value) ? value : default;
  }
}

class Manager 
{
  public int Age { get; set; }
}

class Main34 
{
  long F3() 
  {
    Archive<long> a = new();
    var johnAge = a.GetAt("john");
    if (johnAge is null) return -1; // Error CS0037  Cannot convert null to 'long' because it is a non - nullable value type
    return johnAge; 
  }

  long F4() 
  {
    Archive<Manager> a = new();
    var johnAge = a.GetAt("john");
    //if (johnAge is null) return -1;
    return johnAge.Age; // Correct ! warning "Derefrencing of a possibly null reference" will be removed if line above unremarked 
  }
}

Estou tendo dificuldade em entender / resolver os erros na F3, Parece que o compilador acha johnAge há longnão long?(como eu verificada por pairando sobre ele em VS), apesar do retorno de Archive<T>.GetAtserT?

Existe uma maneira de ter um arquivo genérico que fará o que eu quero (um método GetAt que retorna Nullable mesmo quando T é um tipo básico não anulável, ou seja, longo)?

Respostas

8 JonSkeet Sep 12 2020 at 15:13

Fundamentalmente, isso se resume a tipos de valor anuláveis ​​e tipos de referência anuláveis ​​sendo muito, muito diferentes. O CLR está ciente dos tipos de valor anuláveis, mas no que diz respeito ao CLR, os tipos de referência anuláveis ​​são apenas "o tipo de referência normal, com um atributo informando ao compilador se ele deve ou não ser considerado anulável".

Quando Ttem a notnullrestrição, o tipo T?apenas compila Tno IL. Tem que - não pode compilar Nullable<T>, porque as Nullable<T>restrições Tsão um tipo de valor.

Assim, para um Archive<long>, o GetAtmétodo retornará 0D se a chave não for encontrada no dicionário - que não vai (e não pode) retornar o valor nulo de um Nullable<long>, que é o seu código em F3é efetivamente esperando.

Todo o recurso de "tipos de referência anuláveis" sofre por ser uma tentativa de adicionar um "verniz" de consciência anulável sobre um sistema de tipos que fundamentalmente não o possui. Tenho certeza de que se um novo tempo de execução e linguagem estivessem sendo projetados juntos do zero agora, ele tentaria unificar isso mais de perto. Do jeito que está, acredito que o recurso ainda tem muito valor - mas definitivamente torna as coisas realmente complicadas quando se trata de genéricos.