El lenguaje de programación C# versión 3.0 se lanzó el 19 de noviembre de 2007 como parte de .NET Framework 3.5 . Incluye nuevas características inspiradas en lenguajes de programación funcionales como Haskell y ML , y está impulsado en gran medida por la introducción del patrón Language Integrated Query (LINQ) en Common Language Runtime. [1] Actualmente no está estandarizado por ninguna organización de estándares .
LINQ es un nuevo lenguaje de consulta extensible y de propósito general específico de Microsoft para muchos tipos de fuentes de datos (incluidas colecciones de objetos simples, documentos XML, bases de datos, etc.) que está estrechamente integrado con otras funciones del lenguaje C#. La sintaxis es diferente de SQL , pero toma elementos de esta última . Un ejemplo:
int [] matriz = { 1 , 5 , 2 , 10 , 7 }; // Seleccionar cuadrados de todos los números impares en la matriz ordenados en orden descendente IEnumerable < int > query = from x in array where x % 2 == 1 orderby x descending select x * x ; // Resultado: 49, 25, 1
Para implementar LINQ, se agregó una amplia gama de nuevos métodos a muchas colecciones a través de la System.Linq.Enumerable
clase. Las expresiones LINQ se traducen para usar estas funciones antes de la compilación. Como alternativa, que a veces es más poderosa o directa, se puede acceder a estas funciones directamente. [2] Al hacerlo, se hace un mayor uso de las funciones lambda, que se analizan a continuación. El siguiente ejemplo es funcionalmente idéntico al anterior.
IEnumerable < int > consulta = array . Donde ( x => x % 2 == 1 ) . OrderByDescending ( x => x ) . Select ( x => x * x ); // Resultado: 49, 25, 1 usando 'array' como se define en el ejemplo anterior
Cliente c = nuevo Cliente (); c.Nombre = " Juan " ;
se puede escribir
Cliente c = nuevo Cliente { Nombre = "Juan" };
MiLista lista = nueva MiLista ( ) ; lista.Agregar ( 1 ) ; lista.Agregar ( 2 ) ;
se puede escribir como
Lista MiLista = nueva MiLista { 1 , 2 };
asumiendo que MyList
implementa System.Collections.IEnumerable
y tiene un método público Add
. [3]
Inferencia de tipo de variable local :
var x = nuevo Diccionario < cadena , Lista < float >> ();
es intercambiable con
Diccionario < cadena , Lista < flotante >> x = nuevo Diccionario < cadena , Lista < flotante >> ();
Esta característica no es sólo una sintaxis útil para declaraciones de variables locales más breves, sino que también es necesaria para la declaración de variables de tipos anónimos. Sin embargo, la palabra clave contextual "var" sólo puede aparecer dentro de una declaración de variable local.
Los tipos anónimos proporcionan una forma conveniente de encapsular un conjunto de propiedades de solo lectura en un único objeto sin tener que definir primero explícitamente un tipo. El nombre del tipo lo genera el compilador y no está disponible en el nivel del código fuente. El compilador infiere el tipo de las propiedades.
var x = new { Nombre = "Juan" , Apellido = "Doe" };
Los tipos anónimos son tipos de referencia que derivan directamente de object. El compilador les asigna un nombre, aunque la aplicación no puede acceder a él. Desde la perspectiva de Common Language Runtime, un tipo anónimo no es diferente de cualquier otro tipo de referencia, excepto que no se puede convertir a ningún tipo excepto object.
Si dos o más tipos anónimos tienen el mismo número y tipo de propiedades en el mismo orden, el compilador los trata como el mismo tipo y comparten la misma información de tipo generada por el compilador. [4]
Las expresiones lambda proporcionan una forma concisa de escribir valores de funciones anónimas de primera clase. Compare el siguiente fragmento de código de C# 2.0:
listOfFoo . Donde ( delegado ( Foo x ) { devolver x . Tamaño > 10 ; });
con este equivalente de C# 3.0:
listOfFoo . Donde ( x => x . Tamaño > 10 );
En los ejemplos anteriores, las expresiones lambda son simplemente una sintaxis abreviada para delegados anónimos con inferencia de tipo para parámetros y tipo de retorno. Sin embargo, dependiendo del contexto en el que se utilicen, un compilador de C# también puede transformar las expresiones lambda en AST que luego se pueden procesar en tiempo de ejecución. En el ejemplo anterior, si listOfFoo
no es una simple colección en memoria, sino un contenedor alrededor de una tabla de base de datos, podría usar esta técnica para traducir el cuerpo de la expresión lambda en la expresión SQL equivalente para una ejecución optimizada. De cualquier manera, la expresión lambda en sí se ve exactamente igual en el código, por lo que la forma en que se usa en tiempo de ejecución es transparente para el cliente.
Las expresiones, como x <= y
, a = b + c
, o incluso funciones lambda y otras formas complejas se pueden crear dinámicamente utilizando árboles de expresión . Gran parte de la funcionalidad la proporcionan los métodos estáticos de la clase System.Linq.Expressions.Expression
. También hay varias clases nuevas en ese espacio de nombres que representan las expresiones y expresiones parciales creadas por esos métodos como objetos de software. Estas incluyen BinaryExpression
, que podría representar x <= y
; LambdaExpression
y muchas otras. Cuando se combina con aspectos de la API de reflexión , esta puede ser una herramienta muy poderosa, aunque un poco desafiante para escribir y depurar. [5] [6]
El compilador genera una variable de instancia privada y el accesor y mutador apropiados dado el código como:
cadena pública Nombre { obtener ; conjunto privado ; }
Los desarrolladores pueden usar métodos de extensión para agregar nuevos métodos al contrato público de un tipo CLR existente, sin tener que subclasificarlo o recompilar el tipo original. En realidad, los métodos de extensión son una forma de azúcar sintáctica que proporciona la ilusión de agregar nuevos métodos a la clase existente fuera de su definición. La ilusión se logra con la definición de un método estático que se puede llamar como si fuera un método de instancia, donde el receptor de la llamada (es decir, la instancia) está vinculado al primer parámetro del método, decorado con la palabra clave this
.
Los requisitos para un método de extensión son los siguientes:
this type parameterName
this
parámetro.Esta clase de ejemplo demuestra la definición y el uso de un Left
método de extensión para cadenas:
clase pública estática StringExtensions { cadena pública estática Izquierda ( esta cadena s , int n ) { devuelve s . Subcadena ( 0 , n ); } } cadena s = "foo bar" ; s . Izquierda ( 3 ); // igual que StringExtensions.Left(s, 3), que devuelve "foo";
Los métodos parciales permiten a los generadores de código generar declaraciones de métodos como puntos de extensión que solo se incluyen en la compilación si alguien realmente los implementa en otra parte de una clase parcial. [7]
uso la sintaxis de método más que la sintaxis de consulta porque la sintaxis de consulta es un subconjunto de la sintaxis de método.