Características de C# compatibles con LINQ

Expresiones de consulta

Las expresiones de consulta usan una sintaxis declarativa similar a SQL o XQuery para consultar colecciones de System.Collections.Generic.IEnumerable<T>. En tiempo de compilación, la sintaxis de consulta se convierte en llamadas de método a la implementación de un proveedor LINQ de los métodos de consulta estándar. Las aplicaciones controlan los operadores de consulta estándar que están en el ámbito al especificar el espacio de nombres adecuado con una directiva using. La siguiente expresión de consulta toma una matriz de cadenas, las agrupa por el primer carácter de la cadena y ordena los grupos.

var query = from str in stringArray
            group str by str[0] into stringGroup
            orderby stringGroup.Key
            select stringGroup;

Variables con asignación implícita de tipos (var)

Use el modificador var para indicar al compilador que infiera y asigne el tipo, tal y como se muestra aquí:

var number = 5;
var name = "Virginia";
var query = from str in stringArray
            where str[0] == 'm'
            select str;

Las variables declaradas como var están fuertemente tipadas, como las variables cuyo tipo se especifica explícitamente. El uso de var hace posible crear tipos anónimos, pero solo para variables locales. Para más información, vea Variables locales con asignación implícita de tipos.

Inicializadores de objeto y colección

Los inicializadores de objeto y colección permiten inicializar objetos sin llamar explícitamente a un constructor para el objeto. Los inicializadores normalmente se usan en expresiones de consulta cuando proyectan los datos de origen en un nuevo tipo de datos. Suponiendo que hay una clase denominada Customer con las propiedades públicas Name y Phone, el inicializador de objeto se puede usar como en el código siguiente:

var cust = new Customer { Name = "Mike", Phone = "555-1212" };

Continuando con la clase Customer, suponga que hay un origen de datos denominado IncomingOrders y que para cada orden con un OrderSize grande, quisiera crear un nuevo Customer basado en ese orden. Se pueden ejecutar una consulta LINQ en este origen de datos y usar la inicialización de objetos para rellenar una colección:

var newLargeOrderCustomers = from o in IncomingOrders
                            where o.OrderSize > 5
                            select new Customer { Name = o.Name, Phone = o.Phone };

El origen de datos podría tener más propiedades definidas que la clase Customer, como OrderSize, pero con la inicialización de objetos, los datos devueltos de la consulta se moldean en el tipo de datos deseado. Usted elige los datos pertinentes para la clase. Como resultado, ahora tiene un System.Collections.Generic.IEnumerable<T> relleno con los nuevos Customer que quería. El ejemplo anterior también se puede escribir con la sintaxis del método LINQ:

var newLargeOrderCustomers = IncomingOrders.Where(x => x.OrderSize > 5).Select(y => new Customer { Name = y.Name, Phone = y.Phone });

A partir de C# 12, es posible usar una expresión de colección para inicializar una colección.

Para más información, vea:

Tipos anónimos

El compilador crea un tipo anónimo. El nombre del tipo solo está disponible para el compilador. Los tipos anónimos son una manera cómoda de agrupar un conjunto de propiedades temporalmente en un resultado de consulta sin tener que definir un tipo con nombre independiente. Los tipos anónimos se inicializan con una nueva expresión y un inicializador de objeto, como se muestra aquí:

select new {name = cust.Name, phone = cust.Phone};

A partir de C# 7, es posible usar tuplas para crear tipos sin nombre.

Métodos de extensión.

Un método de extensión es un método estático que se puede asociar con un tipo, por lo que puede llamarse como si fuera un método de instancia en el tipo. Esta característica permite, en efecto, "agregar" nuevos métodos a los tipos existentes sin tener que modificarlos realmente. Los operadores de consulta estándar son un conjunto de métodos de extensión que proporcionan funciones de consultas LINQ para cualquier tipo que implemente IEnumerable<T>.

Expresiones lambda

Una expresión lambda es una función insertada que usa el operador => para separar los parámetros de entrada del cuerpo de la función y que se puede convertir en tiempo de compilación en un delegado o un árbol de (la) expresión. En la programación de LINQ, se encuentran expresiones lambda al realizar llamadas de método directas a los operadores de consulta estándar.

Expresiones como datos

Los objetos de consulta admiten composición, lo que significa que puede devolver una consulta desde un método. Los objetos que representan consultas no almacenan la colección resultante, sino los pasos para generar los resultados cuando sea necesario. La ventaja de devolver objetos de consulta desde métodos es que se pueden componer o modificar todavía más. Por lo tanto, cualquier valor devuelto o parámetro out de un método que devuelve una consulta también debe tener ese tipo. Si un método materializa una consulta en un tipo concreto List<T> o Array, devuelve los resultados de la consulta en lugar de la propia consulta. Una variable de consulta que se devuelve desde un método sigue pudiendo componerse o modificarse.

En el ejemplo siguiente, el primer método QueryMethod1 devuelve una consulta como un valor devuelto y el segundo método QueryMethod2 devuelve una consulta como un parámetro out (returnQ en el ejemplo). En ambos casos, se trata de una consulta que se devuelve, no de los resultados de la consulta.

IEnumerable<string> QueryMethod1(int[] ints) =>
    from i in ints
    where i > 4
    select i.ToString();

void QueryMethod2(int[] ints, out IEnumerable<string> returnQ) =>
    returnQ =
        from i in ints
        where i < 4
        select i.ToString();

int[] nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

var myQuery1 = QueryMethod1(nums);

La consulta myQuery1 se ejecuta en el siguiente bucle foreach.

foreach (var s in myQuery1)
{
    Console.WriteLine(s);
}

Reste el puntero del mouse sobre myQuery1 para ver su tipo.

También puede ejecutar la consulta devuelta desde QueryMethod1 directamente, sin usar myQuery1.

foreach (var s in QueryMethod1(nums))
{
    Console.WriteLine(s);
}

Reste el puntero del mouse sobre la llamada a QueryMethod1 para ver su tipo de valor devuelto.

QueryMethod2 devuelve una consulta como el valor de su parámetro out:

QueryMethod2(nums, out IEnumerable<string> myQuery2);

// Execute the returned query.
foreach (var s in myQuery2)
{
    Console.WriteLine(s);
}

Puede modificar una consulta mediante la composición de la consulta. En este caso, el objeto de consulta anterior se usa para crear un nuevo objeto de consulta. Este nuevo objeto devolverá resultados diferentes a los del objeto de consulta original.

myQuery1 =
    from item in myQuery1
    orderby item descending
    select item;

// Execute the modified query.
Console.WriteLine("\nResults of executing modified myQuery1:");
foreach (var s in myQuery1)
{
    Console.WriteLine(s);
}