La mejor respuesta
El azúcar sintáctico, o azúcar sintáctico, es un «atajo» visual o lógicamente atractivo proporcionado por el lenguaje, que reduce la cantidad de código que debe escribirse en alguna situación común.
Por ejemplo, al establecer una variable en un valor que depende de una condición, en un lenguaje más simple con menos «azúcar», podría hacerlo esto:
int a;
if(SomeFunction() == 2)
a = 4;
else
a = 9;
Esto es demasiado para una operación tan simple; en lenguajes más simples, los desarrolladores a menudo creaban una función «Inline If» para envolver esta lógica en algo más conciso:
public T Iif
{
if(condition) return ifTrue;
return ifFalse;
}
...
//usage
int a = Iif(SomeFunction() == 2, 4, 9);
Ahí, ahora el uso real es mucho más conciso. Muchos lenguajes modernos, incluido C #, incorporan una operación como esta directamente en la especificación del lenguaje en forma de «operador condicional ternario»:
int a = SomeFunction() == 2 ? 4 : 9;
Este operador es un «azúcar sintáctico» proporcionado por el lenguaje para una operación condicional.
Ahora, un sub-caso común de esta configuración de variable condicional es que si nuestra primera opción para un medio para producir un valor para establecer la variable se evalúa como nula, debemos usar un medio alternativo. Por ejemplo:
MyClass theClass = GetMyClass();
if(theClass == null)
theClass = FailSafeMyClassGetter();
No está mal, pero podemos mejorar eso:
MyClass theClass = GetMyClass() ?? FailSafeMyClassGetter();
El «??» El operador se conoce como el «operador de fusión nula» y básicamente produce el primer valor leído de izquierda a derecha que no es nulo.
Otro que C # obtuvo recientemente fue la «propiedad automática». De manera sucinta, C # tiene lo que se llaman «propiedades»; pares de bloques de código que representan un «miembro de datos» de un objeto, que pueden realizar una validación o lógica de cálculo más compleja que la que podría realizar un campo simple, pero a los que se puede acceder como si el miembro fuera un campo en lugar de usar GetX () y SetX () llamadas a métodos, por lo que es fácil diferenciar en el código cuando se accede a datos en lugar de realizar operaciones. Es una buena práctica en C #, por razones de coherencia y mantenimiento, utilizar propiedades en lugar de campos al implementar miembros de datos visibles públicamente . Sin embargo, nueve de cada diez veces, la propiedad «simplemente» envuelve «una variable de campo privado y, por lo tanto, siguiendo las mejores prácticas, la implementación de la propiedad para un valor de datos simple podría verse así:
private int \_myIntProperty;
public int MyIntProperty
{
get
{
return \_myIntProperty;
}
set
{
\_myIntProperty = value;
}
}
Algo feo, muy repetitivo y con muchas más líneas de código de las que realmente necesitamos. En el pasado era tentador renunciar la mejor práctica y solo use un campo en estos casos simples, por lo que para reducir esa tentación y el efecto perjudicial que puede tener en el código base si más tarde decidió que realmente necesitaba una propiedad, los diseñadores de C # incluyeron algo de sintaxis en C # 3.0 especificación:
public int MyIntProperty { get; set; }
Esta declaración de propiedad se trata como si fuera la misma que la declaración anterior, pero es mucho más rápido y fácil escribir una clase llena de estos que hacerlo a la antigua, y también es mucho más fácil de mantener.
Estos son solo algunos ejemplos de «azúcar sintáctico» disponible en C #. Otros incluyen:
* Inferencia de tipo de variable – var someInt = 5;
– El compilador puede determinar el tipo de una variable local declarada a partir de la asignación inicial, por lo que no tiene para especificarlo explícitamente (y luego cambiarlo si el tipo que necesita cambia).
* yield return
– Mucha de la programación .NET involucra colecciones de objetos, y realizar tareas repetitivas en cada elemento del mismo. Solía ser un proceso bastante complicado implementar la interfaz IEnumerable
que permite varios bucles integrados como foreach
para funcionar, lo que requiere una segunda clase completa que implementó IEnumerator
para manejar la iteración a través de la colección. La palabra clave yield return
simplificó drásticamente eso, permitiendo que la propia lógica del iterador se defina dentro del propio método GetEnumerator () de la colección.
* async
/ await
– De manera similar, cada vez más tareas de programación deben ejecutarse «asincrónicamente» dando la trabajar en otro «subproceso» de la aplicación, por lo que el trabajo del programa se puede dividir entre los múltiples procesadores en la PC moderna, mientras que la interfaz de usuario de la aplicación permanece «receptiva» a las solicitudes del sistema operativo para volver a dibujar la interfaz de usuario y realizar otras tareas visuales. El concepto de subprocesamiento no es difícil de entender, pero hasta C # 4.0 esencialmente requería dividir su método en dos; un método inicia la operación asincrónica, luego se llama a un segundo cuando el hilo de fondo ha terminado el trabajo. No más; Estas dos nuevas palabras clave permiten definir una función que realiza un trabajo asíncrono de una manera muy síncrona, mientras que el compilador hace la división de métodos detrás de escena al crear el código.
Respuesta
No existe una definición formal de azúcar sintáctico, esta es una noción nebulosa.
El azúcar sintáctico en su definición más aceptada es una sintaxis extra que el compilador traduce inmediatamente a una forma más primitiva y engorrosa, pero permite una experiencia «más dulce» para el programador al expresar ciertas instrucciones comunes.
El problema con esta definición es que la sintaxis «extra» no es una descripción muy significativa. En ciertos casos, está bastante claro que tenemos azúcar sintáctico, por ejemplo, el informe del lenguaje Haskell define explícitamente ciertos elementos sintácticos (bloques de acción y comprensiones de listas) mediante su traducción en una sintaxis más simple. Este es un caso bastante claro de azúcar sintáctico, existe solo para facilitar la escritura de un programa, no en un sentido profundo en la traducción del compilador del programa a un árbol simbólico.
En otros casos, es mucho menos claro porque el lenguaje no se molesta con este tipo de formalización, por ejemplo, a los decoradores en Python a menudo se les describe cómo escribirías el código equivalente sin ellos, pero hasta donde yo sé, nunca se dice explícitamente: “tú no debería compilar decoradores directamente en su representación intermedia, sino traducirlos de esta forma de antemano ”. Algunas implementaciones de Python pueden optar por hacer precisamente eso, pero otras pueden haber agregado decoradores como una característica de su representación intermedia.