Klauzula From (odwołanie w C#)

Wyrażenie zapytania musi zaczynać się od klauzuli from . Ponadto wyrażenie zapytania może zawierać zapytania podrzędne, które również zaczynają się od klauzuli from . Klauzula from określa następujące elementy:

  • Źródło danych, w którym zostanie uruchomione zapytanie lub zapytanie podrzędne.

  • Lokalna zmienna zakresu, która reprezentuje każdy element w sekwencji źródłowej.

Zarówno zmienna zakresu, jak i źródło danych są silnie typizowane. Źródło danych, do których odwołuje się klauzula from , musi mieć typ IEnumerable, IEnumerable<T>lub typ pochodny, taki jak IQueryable<T>.

W poniższym przykładzie numbers jest źródłem danych i num jest zmienną zakresu. Należy pamiętać, że obie zmienne są silnie typizowane, mimo że jest używane słowo kluczowe 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

Zmienna zakresu

Kompilator wywnioskuje typ zmiennej zakresu, gdy źródło danych implementuje IEnumerable<T>element . Jeśli na przykład źródło ma typ IEnumerable<Customer>, zmienna zakresu jest wnioskowana jako Customer. Jedynym czasem, który należy jawnie określić typ, jest, gdy źródło jest typem niegeneryjnym IEnumerable , takim jak ArrayList. Aby uzyskać więcej informacji, zobacz How to query an ArrayList with LINQ (Jak wykonywać zapytania dotyczące tablicy z linQ).

W poprzednim przykładzie num wywnioskowany jest typ int. Ponieważ zmienna zakresu jest silnie typizowana, można wywołać metody lub użyć jej w innych operacjach. Na przykład zamiast pisać select num, można napisać select num.ToString() , aby spowodować, że wyrażenie zapytania zwróci sekwencję ciągów zamiast liczb całkowitych. Możesz też napisać select num + 10 , aby spowodować zwrócenie wyrażenia sekwencji 14, 11, 13, 12, 10. Aby uzyskać więcej informacji, zobacz klauzulę select.

Zmienna zakresu jest jak zmienna iteracji w instrukcji foreach , z wyjątkiem jednej bardzo ważnej różnicy: zmienna zakresu nigdy nie przechowuje danych ze źródła. Jest to po prostu wygoda składniowa, która umożliwia zapytaniu opisanie tego, co nastąpi po wykonaniu zapytania. Aby uzyskać więcej informacji, zobacz Wprowadzenie do zapytań LINQ (C#).

Złożone z klauzul

W niektórych przypadkach każdy element w sekwencji źródłowej może być sekwencją lub zawierać sekwencję. Na przykład źródło danych może być IEnumerable<Student> miejscem, w którym każdy obiekt ucznia w sekwencji zawiera listę wyników testów. Aby uzyskać dostęp do listy wewnętrznej w każdym Student elemecie, można użyć klauzul złożonych from . Technika ta przypomina używanie zagnieżdżonych instrukcji foreach . Możesz dodać klauzule where lub orderby do klauzuli from , aby filtrować wyniki. Poniższy przykład przedstawia sekwencję Student obiektów, z których każda zawiera wewnętrzne List liczby całkowite reprezentujące wyniki testów. Aby uzyskać dostęp do listy wewnętrznej, użyj klauzuli złożonej from . W razie potrzeby można wstawić klauzule między dwiema from klauzulami.

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
*/

Używanie wielu klauzul from do wykonywania sprzężeń

Klauzula złożona from służy do uzyskiwania dostępu do kolekcji wewnętrznych w jednym źródle danych. Jednak zapytanie może również zawierać wiele from klauzul, które generują dodatkowe zapytania z niezależnych źródeł danych. Ta technika umożliwia wykonywanie niektórych typów operacji sprzężenia, które nie są możliwe przy użyciu klauzuli join.

W poniższym przykładzie pokazano, jak można użyć dwóch from klauzul w celu utworzenia pełnego sprzężenia krzyżowego dwóch źródeł danych.

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
        */

Aby uzyskać więcej informacji na temat operacji sprzężenia używających wielu from klauzul, zobacz Wykonywanie lewymi sprzężeniami zewnętrznymi.

Zobacz też