Clausola from (Riferimento C#)

Un'espressione di query deve iniziare con una clausola from. Inoltre, un'espressione di query può contenere sottoquery che iniziano anch'esse con una clausola from. La clausola from specifica gli elementi seguenti:

  • Origine dati su cui verrà eseguita la query o la sottoquery.

  • Variabile di intervallo locale che rappresenta ogni elemento nella sequenza di origine.

Sia la variabile di intervallo che l'origine dati sono fortemente tipizzate. L'origine dati a cui si fa riferimento nella clausola from deve essere di tipo IEnumerable, IEnumerable<T> o di un tipo derivato, IQueryable<T>.

Nell'esempio seguente numbers è l'origine dati e num è la variabile di intervallo. Si noti che entrambe le variabili sono fortemente tipizzate anche se viene usata la parola chiave var.

class LowNums
{
    static void Main()
    {
        // A simple data source.
        int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];

        // Create the query.
        // lowNums is an IEnumerable<int>
        var lowNums = from num in numbers
            where num < 5
            select num;

        // Execute the query.
        foreach (int i in lowNums)
        {
            Console.Write(i + " ");
        }
    }
}
// Output: 4 1 3 2 0

Variabile di intervallo

Tramite l'inferenza, il compilatore deriva il tipo della variabile di intervallo quando l'origine dati implementa IEnumerable<T>. Se, ad esempio, l'origine è di tipo IEnumerable<Customer>, la variabile di intervallo derivata tramite inferenza sarà Customer. È necessario specificare il tipo in modo esplicito solo quando l'origine è un tipo IEnumerable non generico, ad esempio ArrayList. Per altre informazioni, vedere Come eseguire una query su un ArrayList con LINQ.

Nell'esempio precedente si deriva tramite inferenza che num è di tipo int. Poiché la variabile di intervallo è fortemente tipizzata, è possibile chiamare metodi su di essa o usarla in altre operazioni. Ad esempio, invece di scrivere select num, è possibile scrivere select num.ToString() per fare in modo che l'espressione di query restituisca una sequenza di stringhe invece che di numeri interi. Oppure è possibile scrivere select num + 10 per fare in modo che l'espressione restituisca la sequenza 14, 11, 13, 12 10. Per altre informazioni, vedere Clausola select.

La variabile di intervallo è analoga a una variabile di iterazione in un'istruzione foreach eccetto che per una differenza molto importante: una variabile di intervallo non archivia effettivamente mai i dati dall'origine. Si tratta semplicemente di un pratico aspetto sintattico che consente alla query di descrivere ciò che si verificherà alla sua esecuzione. Per altre informazioni, vedere Introduzione alle query LINQ (C#).

Clausole from composte

In alcuni casi, ogni elemento nella sequenza di origine può essere esso stesso una sequenza o contenere una sequenza. Ad esempio, l'origine dati può essere un oggetto IEnumerable<Student> in cui ogni oggetto studente della sequenza contiene un elenco di punteggi dei test. Per accedere all'elenco interno in ogni elemento Student è possibile usare clausole from composte. La tecnica è analoga all'uso di istruzioni foreach nidificate. È possibile aggiungere clausole where o orderby a una delle due clausole from per filtrare i risultati. L'esempio seguente mostra una sequenza di oggetti Student, ognuno dei quali contiene un oggetto List interno di numeri interi che rappresentano i punteggi dei test. Per accedere all'elenco interno, usare una clausola from composta. Se necessario, è possibile inserire clausole tra le due clausole from.

class CompoundFrom
{
    // The element type of the data source.
    public class Student
    {
        public required string LastName { get; init; }
        public required List<int> Scores {get; init;}
    }

    static void Main()
    {

        // Use a collection initializer to create the data source. Note that
        // each element in the list contains an inner sequence of scores.
        List<Student> students =
        [
           new Student {LastName="Omelchenko", Scores= [97, 72, 81, 60]},
           new Student {LastName="O'Donnell", Scores= [75, 84, 91, 39]},
           new Student {LastName="Mortensen", Scores= [88, 94, 65, 85]},
           new Student {LastName="Garcia", Scores= [97, 89, 85, 82]},
           new Student {LastName="Beebe", Scores= [35, 72, 91, 70]}
        ];

        // Use a compound from to access the inner sequence within each element.
        // Note the similarity to a nested foreach statement.
        var scoreQuery = from student in students
                         from score in student.Scores
                            where score > 90
                            select new { Last = student.LastName, score };

        // Execute the queries.
        Console.WriteLine("scoreQuery:");
        // Rest the mouse pointer on scoreQuery in the following line to
        // see its type. The type is IEnumerable<'a>, where 'a is an
        // anonymous type defined as new {string Last, int score}. That is,
        // each instance of this anonymous type has two members, a string
        // (Last) and an int (score).
        foreach (var student in scoreQuery)
        {
            Console.WriteLine("{0} Score: {1}", student.Last, student.score);
        }
    }
}
/*
scoreQuery:
Omelchenko Score: 97
O'Donnell Score: 91
Mortensen Score: 94
Garcia Score: 97
Beebe Score: 91
*/

Uso di più clausole from per eseguire join

Una clausola from composta viene usata per accedere a raccolte interne in una singola origine dati. Una query può tuttavia contenere anche più clausole from che generano query supplementari da origini dati indipendenti. Questa tecnica consente di eseguire determinati tipi di operazioni di join che non sono possibili usando la clausola join.

L'esempio seguente mostra in che modo due clausole from possono essere usate per formare un cross join completo di due origini dati.

class CompoundFrom2
{
    static void Main()
    {
        char[] upperCase = ['A', 'B', 'C'];
        char[] lowerCase = ['x', 'y', 'z'];

        // The type of joinQuery1 is IEnumerable<'a>, where 'a
        // indicates an anonymous type. This anonymous type has two
        // members, upper and lower, both of type char.
        var joinQuery1 =
            from upper in upperCase
            from lower in lowerCase
            select new { upper, lower };

        // The type of joinQuery2 is IEnumerable<'a>, where 'a
        // indicates an anonymous type. This anonymous type has two
        // members, upper and lower, both of type char.
        var joinQuery2 =
            from lower in lowerCase
            where lower != 'x'
            from upper in upperCase
            select new { lower, upper };

        // Execute the queries.
        Console.WriteLine("Cross join:");
        // Rest the mouse pointer on joinQuery1 to verify its type.
        foreach (var pair in joinQuery1)
        {
            Console.WriteLine("{0} is matched to {1}", pair.upper, pair.lower);
        }

        Console.WriteLine("Filtered non-equijoin:");
        // Rest the mouse pointer over joinQuery2 to verify its type.
        foreach (var pair in joinQuery2)
        {
            Console.WriteLine("{0} is matched to {1}", pair.lower, pair.upper);
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
        Cross join:
        A is matched to x
        A is matched to y
        A is matched to z
        B is matched to x
        B is matched to y
        B is matched to z
        C is matched to x
        C is matched to y
        C is matched to z
        Filtered non-equijoin:
        y is matched to A
        y is matched to B
        y is matched to C
        z is matched to A
        z is matched to B
        z is matched to C
        */

Per altre informazioni sulle operazioni di join che usano più clausole from, vedere Eseguire left outer join.

Vedi anche