Pregunta

Parece que el objeto C # 3.0 inicializador sintaxis permite una para excluir el par de apertura / cierre de paréntesis en el constructor cuando hay un constructor sin parámetros existentes. Ejemplo:

var x = new XTypeName { PropA = value, PropB = value };

A diferencia de:

var x = new XTypeName() { PropA = value, PropB = value };

Tengo curiosidad por qué el constructor de apertura y cierre de paréntesis par / es opcional aquí después XTypeName?

¿Fue útil?

Solución

Esta cuestión fue objeto de mi blog el 20 de septiembre 2010 . Josh y respuestas de Chad ( "añaden ningún valor ¿por qué requerir ellos?" Y "para eliminar la redundancia") son básicamente correcto. Para dar cuerpo que fuera un poco más:

La función de lo que le permite eludir la lista de argumentos, como parte de la "característica más grande" de inicializadores de objeto se reunió nuestra barra de características "azucarados". Algunos puntos que consideran:

  • el coste de diseño y especificación era baja
  • nos va a ser ampliamente cambiar el código analizador que las manijas objeto la creación de todas formas; el coste de desarrollo adicional de hacer la lista de parámetros opcionales no era grande en comparación con el costo de la característica más grande
  • la carga de prueba fue relativamente pequeño en comparación con el coste de la característica más grande
  • la carga de documentación fue relativamente pequeño en comparación ...
  • la carga de mantenimiento se prevé que sea pequeña; No recuerdo ningún errores reportados en esta característica en los años desde que se lo envíen.
  • la característica de no plantea ningún riesgo inmediatamente obvio para funciones futuras en esta área. (La última cosa que queremos hacer es hacer una barata, característica muy fácil ahora que hace que sea mucho más difícil de implementar una característica más atractiva en el futuro.)
  • la característica de no añade nuevas ambigüedades al análisis léxico, gramatical o semántica de la lengua. No plantea problemas para el tipo de análisis "programa parcial" que se realiza por el motor "IntelliSense" del IDE, mientras que está escribiendo. Y así sucesivamente.
  • la función realiza un "punto dulce" común para la función de inicialización del objeto más grande; normalmente si está utilizando un objeto inicializador es precisamente porque el constructor del objeto hace no le permiten establecer las propiedades que desee. Es muy común que este tipo de objetos a ser simplemente "bolsas" de propiedad que no tienen parámetros en el ctor en el primer lugar.
  

¿Por qué, pues, no hace también paréntesis vacíos opcionales en la llamada de constructor por defecto de una expresión de creación del objeto que hace no tener un inicializador de objeto?

Tome otro vistazo a la lista de los criterios anteriores. Uno de ellos es que el cambio no introduce ninguna nueva ambigüedad en el análisis léxico, gramatical o semántica de un programa. Su cambio propuesto ¿Tiene introducen una ambigüedad análisis semántico:

class P
{
    class B
    {
        public class M { }
    }
    class C : B
    {
        new public void M(){}
    }
    static void Main()
    {
        new C().M(); // 1
        new C.M();   // 2
    }
}

La línea 1 crea un nuevo C, llama al constructor por defecto, y luego llama al método de instancia M en el nuevo objeto. La línea 2 crea una nueva instancia de B.M y llama a su constructor por defecto. Si los paréntesis en la línea 1 eran opcionales luego la línea 2 sería ambigua Entonces tendríamos que llegar a una regla de resolución de la ambigüedad.; no podíamos hacer que sea un error porque eso sería entonces un cambio importante que cambia de un programa en C # legal existente en un programa roto.

Por lo tanto la regla tendría que ser muy complicado: en esencia, que los paréntesis son opcionales solamente en los casos en que no se introducen ambigüedades. Tendríamos que analizar todos los posibles casos que introducen ambigüedades y luego escribir código en el compilador para detectarlos.

En este sentido, volver atrás y mirar a todos los gastos que menciono. ¿Cuántos de ellos ahora se hacen grandes? reglas complicadas tienen grandes costes de diseño, especificación, desarrollo, prueba y documentación. reglas complicadas son mucho más propensos a causar problemas con las interacciones inesperadas con características en el futuro.

Todo para qué? Un pequeño beneficio para el cliente que no añade ningún nuevo poder de representación de la lengua, pero que añadir casos de esquina locos a la espera de gritar "Gotcha" en algún pobre alma inocente que corre en ella. Características como la que se cortan inmediatamente y pusieron en el "Nunca hacer esto" lista.

  

¿Cómo determinó que la ambigüedad en particular?

Ese uno era inmediatamente claro; Estoy bastante familiarizado con las reglas de C # para determinar cuándo se espera un nombre de puntos.

  

Al considerar una nueva característica, ¿cómo determinar si causa alguna ambigüedad? A mano, por la prueba formal, mediante el análisis de la máquina, qué?

Los tres. En su mayoría sólo miramos la especificación y fideos en él, como lo hice anteriormente. Por ejemplo, supongamos que deseamos añadir un nuevo operador de prefijo a C # llamado "FROB":

x = frob 123 + 456;

(ACTUALIZACIÓN:. frob es de await supuesto, el análisis aquí es esencialmente el análisis que el equipo de diseño pasó por al agregar await)

"FROB" aquí es como "nuevas" o "++" - se trata antes de una expresión de algún tipo. Nos gustaría trabajar en los precedencia y asociatividad deseado y así sucesivamente, y luego empezar a hacer preguntas como "¿y si el programa ya tiene un tipo, campo, propiedad evento, método, constante o llamada FROB local?" Eso llevaría inmediatamente a casos como:

frob x = 10;

quiere decir eso "hacer la operación FROB en el resultado de x = 10, o crear una variable de tipo FROB llamados x 10 y asignar a ella?" (O, si frobbing produce una variable, podría ser una asignación de 10 a frob x. Después de todo, lo analiza y *x = 10; es legal si es x int*.)

G(frob + x)

¿Eso significa "FROB el resultado del operador unario mas de x" o "añadir FROB expresión a x"?

Y así sucesivamente. Para resolver estas ambigüedades que podríamos introducir la heurística. Cuando se dice "var x = 10;" eso es ambigua; que podría significar "inferir el tipo de x" o podría significar "x es de tipo var". Así que tenemos una heurística: en primer lugar tratar de buscar un tipo con nombre var, y sólo si no existe uno que hacer inferir el tipo de x.

O, que podría cambiar la sintaxis de modo que no es ambigua. Cuando diseñaron C # 2.0 que tenían este problema:

yield(x);

¿Eso significa "rendimiento x en un repetidor" o "llamar al método de rendimiento con el argumento x?" Cambiándola a

yield return(x);

ahora es inequívoca.

En el caso de parens opcionales en un inicializador de objeto es sencillo razonar acerca de si existen ambigüedades introducidas o no, porque el número de situaciones en las que se permite introducir algo que comienza con {es muy pequeño . Básicamente, sólo diferentes contextos declaración, lambdas declaración, inicializadores de matriz y eso es todo. Es fácil a la razón a través de todos los casos y mostrar que no hay ambigüedad. Asegurándose de que las estancias IDE eficiente es algo más difícil, pero se puede hacer sin demasiados problemas.

Este tipo de perder el tiempo con la especificación por lo general es suficiente. Si se trata de una característica particularmente complicado luego nos retiramos herramientas más pesadas. Por ejemplo, en el diseño de LINQ, uno de los chicos del compilador y uno de los chicos IDE que ambos tienen un fondo en la teoría analizador mismos construyó un generador de filtros que podrían analizar gramáticas en busca de ambigüedades, y luego # gramáticas C propuestos alimentados para comprensiones de consulta en ella ; al hacerlo se han encontrado muchos casos donde las consultas fueron ambiguos.

O, cuando hicimos la inferencia tipo avanzado de lambda en C # 3.0 escribimos nuestras propuestas y luego los envía a través de la laguna de Microsoft Research en Cambridge, donde el equipo de lenguas no era bueno suficiente para trabajar hasta una prueba formal de que el tipo inferencia propuesta fue teóricamente sonido.

  

¿Hay ambigüedades en C # hoy?

Claro.

G(F<A, B>(0))

En C # 1, está claro lo que eso significa. Es lo mismo que:

G( (F<A), (B>0) )

Esto es, se llama G con dos argumentos que son Bools. En C # 2, que podría significar lo que significaba en C # 1, pero podría también significan "pase 0 para el método genérico F que toma tipoparámetros A y B y, a continuación, pasar el resultado de F a G". Añadimos una heurística compleja al analizador que determina cuál de los dos casos es probable que significaba.

Del mismo modo, los moldes son ambiguas incluso en C # 1.0:

G((T)-x)

es que "fundido a T-x" o "x Restar de la T"? Una vez más, tenemos una heurística que hace que una buena conjetura.

Otros consejos

porque así es como se ha especificado el idioma. Añaden ningún valor, ¿por qué incluirlos?

También es muy similar a implícitamente Conjuntos escritos

var a = new[] { 1, 10, 100, 1000 };            // int[]
var b = new[] { 1, 1.5, 2, 2.5 };            // double[]
var c = new[] { "hello", null, "world" };      // string[]
var d = new[] { 1, "one", 2, "two" };         // Error

Referencia: http://msdn.microsoft .com / es-es / library / ms364047% 28VS.80% 29.aspx

Esto se hizo para simplificar la construcción de objetos. Los diseñadores del lenguaje no tienen (que yo sepa) dicho específicamente por qué pensaban que esto era útil, aunque se menciona explícitamente en el C # Version 3.0 Especificación página :

  

Una expresión creación de objetos puede omitir la lista de argumentos del constructor y los paréntesis que encierran, siempre que incluya un objeto o una colección de inicialización. La omisión de la lista de argumentos del constructor y que encierra entre paréntesis es equivalente a especificar una lista de argumentos vacía.

I supongamos que sentían el paréntesis, en este caso, no eran necesarias con el fin de mostrar desarrollador intención, ya que el objeto inicializador muestra la intención de construir y establecer las propiedades del objeto en su lugar.

En el primer ejemplo, se infiere que el compilador que está llamando el constructor por defecto (C # 3.0 Especificación del lenguaje establece que si se proporcionan sin paréntesis, el constructor por defecto se llama).

En el segundo, se llama explícitamente el constructor por defecto.

También puede utilizar la sintaxis de propiedades de deformación al pasar valores explícitamente al constructor. Si usted tuvo la siguiente definición de clase:

public class SomeTest
{
    public string Value { get; private set; }
    public string AnotherValue { get; set; }
    public string YetAnotherValue { get; set;}

    public SomeTest() { }

    public SomeTest(string value)
    {
        Value = value;
    }
}

Las tres afirmaciones son válidas:

var obj = new SomeTest { AnotherValue = "Hello", YetAnotherValue = "World" };
var obj = new SomeTest() { AnotherValue = "Hello", YetAnotherValue = "World"};
var obj = new SomeTest("Hello") { AnotherValue = "World", YetAnotherValue = "!"};

No soy Eric Lippert, así que no puedo decir con certeza, pero yo supongo que es porque el paréntesis vacío no es necesaria por el compilador con el fin de inferir la construcción de la inicialización. Por lo tanto se convierte en información redundante y no es necesario.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top