Compartilhar via


Expressões new com tipo de destino

Nota

Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ele inclui alterações de especificação propostas, juntamente com as informações necessárias durante o design e o desenvolvimento do recurso. Esses artigos são publicados até que as alterações de especificação 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 reunião de design de idioma (LDM).

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

Problema do especialista: https://github.com/dotnet/csharplang/issues/100

Resumo

Não exija especificação de tipo para construtores quando o tipo for conhecido.

Motivação

Permitir inicialização de campo sem duplicar o tipo.

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

Permitir omitir o tipo quando ele puder ser inferido do uso.

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

Instancie um objeto sem especificar o tipo.

private readonly static object s_syncObj = new();

Especificação

Um novo formato sintático, target_typed_new da object_creation_expression é aceito no 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 de expressão, que existe de um target_typed_new para cada 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 será T. O significado de uma expressão target_typed_new que é convertida no tipo T é o mesmo que o significado de um object_creation_expression correspondente que especifica T0 como o tipo.

Será 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 utilizado em contextos nos quais não está sujeito a uma conversão de criação de objeto .

Problema Aberto: deveríamos permitir delegados e tuplas como tipo de destino?

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

(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

Variado

Veja a seguir as consequências da especificação:

  • throw new() é permitido (o tipo de destino é System.Exception)
  • O tipo de destino new não é permitido com operadores binários.
  • Não é permitido quando não há tipo para ser direcionado: operadores unários, coleção de uma foreach, em um using, em 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 acesso de membro (new().field), em uma operação dinamicamente despachada (someDynamic.Method(new())), em uma consulta LINQ, como o operando do operador is, como o operando esquerdo do operador ??, ...
  • Também é proibido como um ref.
  • Os tipos a seguir não são permitidos como destinos da conversão
    • tipos de enumeração:new() funcionará (como new Enum() funciona para fornecer o valor padrão), mas new(1) não funcionará, pois os tipos de enumeração não têm um 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âmico: não permitimos new dynamic(), portanto, não permitimos new() com dynamic como um tipo de destino.
    • tuplas: elas têm o mesmo significado que a criação de um objeto usando o tipo subjacente.
    • Todos os outros tipos que não são permitidos na expressão de criação de objetos também são excluídos, como, por exemplo, tipos ponteiros.

Inconvenientes

Houve algumas preocupações com o tipo de destino new a criação de novas categorias de alterações interruptivas, mas já temos isso com null e default, e isso não tem sido um problema significativo.

Alternativas

A maioria das reclamações sobre os tipos serem muito longos para duplicação na inicialização de campos refere-se aos argumentos do tipo , não ao tipo em si. Poderíamos inferir apenas os argumentos do tipo, como new Dictionary(...) (ou similar), e inferir argumentos do tipo localmente a partir dos argumentos ou do inicializador da coleção.

Perguntas

  • Devemos proibir o uso em árvores de expressão? (não)
  • Como a funcionalidade interage com argumentos dynamic? (sem tratamento especial)
  • Como o IntelliSense deve trabalhar com new()? (somente quando houver um único tipo de destino)

Reuniões de Design