Partilhar via


Expressões de new tipo de destino

Observação

Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ele inclui mudanças de especificação propostas, juntamente com as informações necessárias durante o design e desenvolvimento do recurso. Estes artigos são publicados até que as alterações de especificações propostas sejam finalizadas e incorporadas na especificação ECMA atual.

Pode haver algumas discrepâncias entre a especificação do recurso e a implementação concluída. Essas diferenças são capturadas nas notas pertinentes da Language Design Meeting (LDM).

Você pode saber mais sobre o processo de adoção de especificações de recursos no padrão de linguagem C# no artigo sobre as especificações .

Questão campeã: https://github.com/dotnet/csharplang/issues/100

Resumo

Não requer especificação de tipo para construtores quando o tipo é conhecido.

Motivação

Permitir a inicialização do campo sem duplicar o tipo.

Dictionary<string, List<int>> field = new() {
    { "item1", new() { 1, 2, 3 } }
};

Permitir omitir o tipo quando ele pode ser inferido a partir do uso.

XmlReader.Create(reader, new() { IgnoreWhitespace = true });

Instanciar um objeto sem especificar o tipo.

private readonly static object s_syncObj = new();

Especificação

Aceita-se uma nova forma sintática, target_typed_new do object_creation_expression, na qual o tipo é opcional.

object_creation_expression
    : 'new' type '(' argument_list? ')' object_or_collection_initializer?
    | 'new' type object_or_collection_initializer
    | target_typed_new
    ;
target_typed_new
    : 'new' '(' argument_list? ')' object_or_collection_initializer?
    ;

Uma expressão target_typed_new não tem um tipo. No entanto, há uma nova conversão de criação de objeto que é uma conversão implícita a partir de expressão, que existe de um target_typed_new para qualquer tipo.

Dado um tipo-alvo T, o tipo T0 é o tipo subjacente de Tse T for uma instância de System.Nullable. Caso contrário, T0 é T. O significado de uma expressão target_typed_new que é convertida para o tipo T é o mesmo que o significado de um object_creation_expression correspondente que especifica T0 como o tipo.

É um erro em tempo de compilação se um target_typed_new for usado como um operando de um operador unário ou binário, ou se for usado onde não está sujeito a uma conversão de criação de objeto.

Questão em aberto: devemos permitir delegados e tuplas como o tipo alvo?

As regras acima incluem delegados (um tipo de referência) e tuplas (um tipo de estrutura). Embora ambos os tipos sejam construíveis, se o tipo for inferível, uma função anônima ou um literal de tupla já podem ser usados.

(int a, int b) t = new(1, 2); // "new" is redundant
Action a = new(() => {}); // "new" is redundant

(int a, int b) t = new(); // OK; same as (0, 0)
Action a = new(); // no constructor found

Diversos

As consequências da especificação são as seguintes:

  • throw new() é permitido (o tipo de destino é System.Exception)
  • Tipos-alvo new não são permitidos com operadores binários.
  • Não é permitido quando não há tipo a ser visado: operadores unários, coleta de um foreach, em uma using, em uma desconstrução, em uma expressão await, como uma propriedade de tipo anônimo (new { Prop = new() }), em uma instrução lock, em um sizeof, em uma instrução fixed, em um acesso de membro (new().field), em uma operação despachada dinamicamente (someDynamic.Method(new())), em uma consulta LINQ, como o operando do operador is, como o operando esquerdo do operador ??, ...
  • Também não é permitido como ref.
  • Os seguintes tipos de tipos não são permitidos como alvos da conversão
    • Tipos de Enum:new() funcionará (tal como new Enum() funciona para dar o valor padrão), mas new(1) não funcionará, pois os tipos de enum não têm construtor.
    • Tipos de interface: Isso funcionaria da mesma forma que a expressão de criação correspondente para tipos COM.
    • Tipos de matriz: matrizes precisam de uma sintaxe especial para fornecer o comprimento.
    • dinâmica: não permitimos new dynamic(), por isso não permitimos new() com dynamic como tipo de destino.
    • tuplas: Estes têm o mesmo significado que uma criação de objeto usando o tipo subjacente.
    • Todos os outros tipos que não são permitidos na expressão de criação de objeto também são excluídos, por exemplo, tipos de ponteiro.

Desvantagens

Havia algumas preocupações com new de tipo alvo criando novas categorias de mudanças de rutura, mas já temos isso com null e default, e isso não tem sido um problema significativo.

Alternativas

A maioria das reclamações de que os tipos são demasiado longos para duplicar na inicialização de campo refere-se apenas aos argumentos de tipo como e não ao tipo em si. No entanto, podemos optar por inferir apenas os argumentos de tipo como new Dictionary(...) (ou similar) e inferir argumentos de tipo localmente a partir dos argumentos ou a partir do inicializador de coleção.

Perguntas

  • Devemos proibir utilizações em árvores de expressões? (não)
  • Como é que a funcionalidade interage com os argumentos dynamic? (sem tratamento especial)
  • Como o IntelliSense deve funcionar com new()? (apenas quando existe um único tipo de destino)

Reuniões de design