Sintaxis de consultas y Sintaxis de métodos (LINQ)

Actualización: noviembre 2007

La mayoría de las consultas de la documentación de introducción a LINQ están escritas como expresiones de consulta, utilizando la sintaxis de consulta declarativa que se dio a conocer en C# 3.0. Sin embargo, .NET Common Language Runtime (CLR) no reconoce por sí mismo la sintaxis de consulta. Por consiguiente, en tiempo de compilación, las expresiones de consulta se convierten en elementos que CLR reconoce, esto es, llamadas a método. Estos métodos se conocen como operadores de consulta estándar y tienen nombres como Where, Select, GroupBy, Join, Max, Average, etc. Se pueden llamar directamente utilizando sintaxis de método en lugar de sintaxis de consulta.

Por lo general, recomendamos la sintaxis de consulta porque normalmente es más fácil y legible; sin embargo, no hay ninguna diferencia semántica entre la sintaxis de método y la sintaxis de consulta. Además, algunas consultas, como las que recuperan el número de elementos que coinciden con una condición especificada o las que recuperan el elemento que tiene el valor máximo de una secuencia de origen, sólo se pueden expresar como llamadas a método. En la documentación de referencia de los operadores de consulta estándar en el espacio de nombres System.Linq generalmente se utiliza la sintaxis de método. Por consiguiente, aunque esté empezando a escribir consultas LINQ, le resultará útil estar familiarizado con el uso de la sintaxis de método en consultas y en expresiones de consulta.

Métodos de extensión de operador de consulta estándar

En el ejemplo siguiente se muestra una expresión de consulta simple y la consulta semánticamente equivalente, escrita como consulta basada en método.

class QueryVMethodSyntax
{
    static void Main()
    {
        int[] numbers = { 5, 10, 8, 3, 6, 12};

        //Query syntax:
        IEnumerable<int> numQuery1 = 
            from num in numbers
            where num % 2 == 0
            orderby num
            select num;

        //Method syntax:
        IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);

        foreach (int i in numQuery1)
        {
            Console.Write(i + " ");
        }
        Console.WriteLine(System.Environment.NewLine);
        foreach (int i in numQuery2)
        {
            Console.Write(i + " ");
        }

        // Keep the console open in debug mode.
        Console.WriteLine(System.Environment.NewLine);
        Console.WriteLine("Press any key to exit");
        Console.ReadKey();
    }
}
/*
    Output:
    6 8 10 12
    6 8 10 12
 */

El resultado de los dos ejemplos es idéntico. Puede ver que el tipo de la variable de consulta es el mismo en ambos formatos: IEnumerable<T>.

Para entender la consulta basada en método, examinémosla más de cerca. En el lado derecho de la expresión, observe que la cláusula where se expresa ahora como un método de instancia en el objeto numbers, que, como recordará, es de tipo IEnumerable<int>. Si está familiarizado con la interfaz genérica IEnumerable<T>, sabrá que no tiene un método Where. Sin embargo, si invoca la lista de finalización de IntelliSense en el IDE de Visual Studio, verá no sólo un método Where, sino muchos otros métodos, como Select, SelectMany, Join y Orderby. Éstos son todos los operadores de consulta estándar.

Operadores de consulta estándar en Intellisense

Aunque parece que IEnumerable<T> se ha redefinido para incluir estos métodos adicionales, no es así. Los operadores de consulta estándar se implementan como un nuevo tipo de método denominado método de extensión. Los métodos de extensión "extienden" un tipo existente; se pueden llamar como si fueran métodos de instancia en el tipo. Los operadores de consulta estándar extienden IEnumerable<T> y por eso puede escribir numbers.Where(...).

Para empezar a utilizar LINQ, todo lo que realmente tiene que saber sobre los métodos de extensión es cómo incluirlos en el ámbito en su aplicación utilizando las directivas using correctas. Esto se explica además en Cómo: Crear un proyecto con LINQ. Desde el punto de vista de la aplicación, un método de extensión y un método de instancia normal son iguales.

Para obtener más información acerca de los métodos de extensión, vea Métodos de extensión (Guía de programación de C#). Para obtener más información sobre los operadores de consulta estándar, vea Guía de programación general con LINQ y Información general sobre operadores de consulta estándar. Algunos proveedores LINQ, como LINQ to SQL y LINQ to XML, implementan sus propios operadores de consulta estándar y métodos de extensión adicionales para otros tipos además de IEnumerable<T>

Expresiones lambda

En el ejemplo anterior, observe cómo la expresión condicional (num % 2 == 0) se pasa como argumento en línea al método Where: Where(num => num % 2 == 0). Esta expresión insertada se denomina expresión lambda. Es una manera sencilla de escribir código que de lo contrario sería más complejo y debería escribirse como método anónimo, delegado genérico o árbol de expresión. En C#, => es el operador lambda, que se lee como "va a". El elemento num que está a la izquierda del operador es la variable de entrada que corresponde a num en la expresión de consulta. El compilador puede deducir el tipo de num porque sabe que numbers es un tipo IEnumerable<T> genérico. El cuerpo de una expresión lambda es exactamente igual que el de una expresión en sintaxis de consulta o cualquier otra expresión o instrucción de C#; puede incluir llamadas a método y otra lógica compleja. El "valor devuelto" es tan solo el resultado de la expresión.

Para iniciarse en LINQ, no es necesario utilizar muchas expresiones lambda. Sin embargo, ciertas consultas sólo se pueden expresar en sintaxis de método y algunas requieren el uso de expresiones lambda. Una vez que esté más familiarizado con las expresiones lambda, verá que son una herramienta eficaz y flexible en su cuadro de herramientas LINQ. Para obtener más información, consulte Expresiones lambda (Guía de programación de C#).

Combinabilidad de las consultas

En el ejemplo de código anterior, observe cómo se invoca al método OrderBy utilizando el operador de punto en la llamada a Where. Where genera una secuencia filtrada y, después, Orderby actúa sobre esa secuencia para ordenarla. Dado que las consultas devuelven IEnumerable, en la sintaxis de método se combinan concatenando las llamadas a método. Esto es lo que el compilador hace en segundo plano cuando se escriben consultas en sintaxis de consulta. Y, dado que una variable de consulta no almacena los resultados de la consulta, puede modificarla o utilizarla en cualquier momento como base para una nueva consulta, incluso después de haberla ejecutado.

Vea también

Otros recursos

Introducción a LINQ en C#