Compartir vía


Expresiones new con tipo de destino

Nota

Este artículo es una especificación de características. La especificación actúa como documento de diseño de la característica. Incluye cambios de especificación propuestos, junto con la información necesaria durante el diseño y el desarrollo de la característica. Estos artículos se publican hasta que se finalizan los cambios de especificación propuestos e se incorporan en la especificación ECMA actual.

Puede haber algunas discrepancias entre la especificación de características y la implementación completada. Esas diferencias se recogen en las notas de la reunión de diseño de lenguaje (LDM) correspondientes.

Puede obtener más información sobre el proceso de adopción de especificaciones de características en el estándar del lenguaje C# en el artículo sobre las especificaciones de .

Problema planteado por el experto: https://github.com/dotnet/csharplang/issues/100

Resumen

No requiera especificación de tipo para constructores cuando se conoce el tipo.

Motivación

Permitir la inicialización de campos sin duplicar el tipo.

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

Permitir omitir el tipo cuando se puede deducir del uso.

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

Instanciar un objeto sin deletrear el tipo.

private readonly static object s_syncObj = new();

Especificación

Se acepta una nueva forma sintáctica, target_typed_new de la object_creation_expression en la que el tipo de es 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?
    ;

Una expresión target_typed_new no tiene un tipo. Sin embargo, hay una nueva conversión de creación de objeto que es una conversión implícita de expresión, que existe de un target_typed_new a cada tipo.

Dado un tipo de destino T, el tipo subyacente de Tes el tipo T0 si T es una instancia de System.Nullable. De lo contrario, T0 es T. El significado de una expresión de target_typed_new que se convierte en el tipo T es el mismo que el significado de un object_creation_expression correspondiente que especifica T0 como tipo.

Es un error en tiempo de compilación si un target_typed_new se utiliza como operando de un operador unario o binario, o si se utiliza donde no está sujeto a una conversión de creación de objeto.

Cuestión abierta: ¿deberíamos permitir delegados y tuplas como tipo de destino?

Las reglas anteriores incluyen delegados (un tipo de referencia) y tuplas (un tipo struct). Aunque ambos tipos son construibles, si el tipo es inferible, ya se puede utilizar una función anónima o un literal de tupla.

(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

Misceláneo

A continuación se muestran las consecuencias de la especificación:

  • Se permite throw new() (el tipo de destino es System.Exception)
  • El tipo objetivo new no está permitido con operadores binarios.
  • No está permitido cuando no hay un tipo al que apuntar: operadores unarios, colección de un foreach, en un using, en una deconstrucción, en una expresión await, como una propiedad de tipo anónimo (new { Prop = new() }), en una instrucción lock, en un sizeof, en una instrucción fixed, en un acceso a miembro (new().field), en una operación despachada dinámicamente (someDynamic.Method(new())), en una consulta LINQ, como operando del operador is, como operando izquierdo del operador ??, etc.
  • Tampoco está permitido como ref.
  • No se permiten los siguientes tipos de tipos como destinos de la conversión.
    • tipos de enumeración:new() funcionará (ya que new Enum() funciona para proporcionar el valor predeterminado), pero new(1) no funcionará porque los tipos de enumeración no tienen constructor.
    • Tipos de interfaz: Esto funcionaría igual que la expresión de creación correspondiente para los tipos COM.
    • Tipos de matriz: matrices necesitan una sintaxis especial para proporcionar la longitud.
    • dinámico: no permitimos new dynamic(), por lo que no permitimos new() con dynamic como tipo destino.
    • tuplas: Estas tienen el mismo significado que la creación de un objeto utilizando el tipo subyacente.
    • En el object_creation_expression se excluyen también todos los demás tipos que no están permitidos, como, por ejemplo, los tipos de puntero.

Inconvenientes

Hubo algunas preocupaciones con tipo-objetivo new creando nuevas categorías de cambios de ruptura, pero ya tenemos que con null y default, y que no ha sido un problema significativo.

Alternativas

La mayoría de las quejas sobre los tipos de ser demasiado largo para duplicar en la inicialización de campo es sobre argumentos de tipo no el tipo en sí, podríamos inferir solo tipo argumentos como new Dictionary(...) (o similar) e inferir tipo argumentos localmente de argumentos o el inicializador de colección.

Preguntas

  • ¿Deberíamos prohibir los usos en árboles de expresión? (no)
  • ¿Cómo interactúa la característica con los argumentos dynamic? (sin tratamiento especial)
  • ¿Cómo debe funcionar IntelliSense con new()? (Solo cuando hay un único tipo de destino).

Reuniones de diseño