Projeksiyon işlemleri (C#)

Projeksiyon, bir nesneyi genellikle yalnızca daha sonra kullanılan özelliklerden oluşan yeni bir forma dönüştürme işlemini ifade eder. Projeksiyonu kullanarak, her nesneden oluşturulan yeni bir tür oluşturabilirsiniz. Bir özelliği yansıtabilir ve üzerinde matematiksel bir işlev gerçekleştirebilirsiniz. Özgün nesneyi değiştirmeden de yansıtabilirsiniz.

Önemli

Bu örnekler bir System.Collections.Generic.IEnumerable<T> veri kaynağı kullanır. Veri kaynaklarını ve ifade ağaçlarını System.Linq.IQueryProviderkullanan System.Linq.IQueryable<T> veri kaynakları. İfade ağaçlarının izin verilen C# söz diziminde sınırlamaları vardır. Ayrıca EF Core gibi her IQueryProvider veri kaynağı daha fazla kısıtlama uygulayabilir. Veri kaynağınızın belgelerine bakın.

Projeksiyon gerçekleştiren standart sorgu işleci yöntemleri aşağıdaki bölümde listelenmiştir.

Yöntemler

Yöntem adları Açıklama C# sorgu ifadesi söz dizimi Daha Fazla Bilgi
Seç Bir dönüştürme işlevini temel alan değerleri projeler. select Enumerable.Select
Queryable.Select
Selectmany Bir dönüştürme işlevini temel alan değer dizilerini projeler ve sonra bunları tek bir sırayla düzleştirir. Birden çok from yan tümce kullanma Enumerable.SelectMany
Queryable.SelectMany
Posta Kodu Belirtilen 2-3 diziden öğeler içeren bir demet dizisi oluşturur. Uygulanamaz. Enumerable.Zip
Queryable.Zip

Select

Aşağıdaki örnek, dize listesindeki her dizeden ilk harfi yansıtmak için yan tümcesini kullanır select .

List<string> words = ["an", "apple", "a", "day"];

var query = from word in words
            select word.Substring(0, 1);

foreach (string s in query)
{
    Console.WriteLine(s);
}

/* This code produces the following output:

    a
    a
    a
    d
*/

Yöntem söz dizimini kullanan eşdeğer sorgu aşağıdaki kodda gösterilir:

List<string> words = ["an", "apple", "a", "day"];

var query = words.Select(word => word.Substring(0, 1));

foreach (string s in query)
{
    Console.WriteLine(s);
}

/* This code produces the following output:

    a
    a
    a
    d
*/

SelectMany

Aşağıdaki örnek, her dizedeki her sözcüğü bir dize listesinde yansıtmak için birden çok from yan tümce kullanır.

List<string> phrases = ["an apple a day", "the quick brown fox"];

var query = from phrase in phrases
            from word in phrase.Split(' ')
            select word;

foreach (string s in query)
{
    Console.WriteLine(s);
}

/* This code produces the following output:

    an
    apple
    a
    day
    the
    quick
    brown
    fox
*/

Yöntem söz dizimini kullanan eşdeğer sorgu aşağıdaki kodda gösterilir:

List<string> phrases = ["an apple a day", "the quick brown fox"];

var query = phrases.SelectMany(phrases => phrases.Split(' '));

foreach (string s in query)
{
    Console.WriteLine(s);
}

/* This code produces the following output:

    an
    apple
    a
    day
    the
    quick
    brown
    fox
*/

yöntemi, SelectMany ilk sıradaki her öğeyi ikinci sıradaki her öğeyle eşleştirmenin birleşimini de oluşturabilir:

var query = from number in numbers
            from letter in letters
            select (number, letter);

foreach (var item in query)
{
    Console.WriteLine(item);
}

Yöntem söz dizimini kullanan eşdeğer sorgu aşağıdaki kodda gösterilir:

var method = numbers
    .SelectMany(number => letters,
    (number, letter) => (number, letter));

foreach (var item in method)
{
    Console.WriteLine(item);
}

Zip

Projeksiyon işleci için Zip çeşitli aşırı yüklemeler vardır. Zip Tüm yöntemler, iki veya daha fazla muhtemelen heterojen türün dizileri üzerinde çalışır. İlk iki aşırı yükleme, verilen dizilerden karşılık gelen konumsal türle birlikte tanımlama demetleri döndürür.

Aşağıdaki koleksiyonları göz önünde bulundurun:

// An int array with 7 elements.
IEnumerable<int> numbers = [1, 2, 3, 4, 5, 6, 7];
// A char array with 6 elements.
IEnumerable<char> letters = ['A', 'B', 'C', 'D', 'E', 'F'];

Bu dizileri birlikte yansıtmak için şu işlecini Enumerable.Zip<TFirst,TSecond>(IEnumerable<TFirst>, IEnumerable<TSecond>) kullanın:

foreach ((int number, char letter) in numbers.Zip(letters))
{
    Console.WriteLine($"Number: {number} zipped with letter: '{letter}'");
}
// This code produces the following output:
//     Number: 1 zipped with letter: 'A'
//     Number: 2 zipped with letter: 'B'
//     Number: 3 zipped with letter: 'C'
//     Number: 4 zipped with letter: 'D'
//     Number: 5 zipped with letter: 'E'
//     Number: 6 zipped with letter: 'F'

Önemli

Zip işleminden elde edilen sıra hiçbir zaman en kısa diziden uzun olmaz. numbers ve letters koleksiyonlarının uzunluğu farklıdır ve sonuçta elde edilen dizi, sıkıştırması gerekmeyen numbers son öğeyi koleksiyondan atlar.

İkinci aşırı yükleme bir third dizi kabul eder. Şimdi başka bir koleksiyon oluşturalım:emoji

// A string array with 8 elements.
IEnumerable<string> emoji = [ "🤓", "🔥", "🎉", "👀", "⭐", "💜", "✔", "💯"];

Bu dizileri birlikte yansıtmak için şu işlecini Enumerable.Zip<TFirst,TSecond,TThird>(IEnumerable<TFirst>, IEnumerable<TSecond>, IEnumerable<TThird>) kullanın:

foreach ((int number, char letter, string em) in numbers.Zip(letters, emoji))
{
    Console.WriteLine(
        $"Number: {number} is zipped with letter: '{letter}' and emoji: {em}");
}
// This code produces the following output:
//     Number: 1 is zipped with letter: 'A' and emoji: 🤓
//     Number: 2 is zipped with letter: 'B' and emoji: 🔥
//     Number: 3 is zipped with letter: 'C' and emoji: 🎉
//     Number: 4 is zipped with letter: 'D' and emoji: 👀
//     Number: 5 is zipped with letter: 'E' and emoji: ⭐
//     Number: 6 is zipped with letter: 'F' and emoji: 💜

Önceki aşırı yükleme gibi yöntemi Zip de bir tanımlama grubu oluşturur, ancak bu kez üç öğeyle.

Üçüncü aşırı yükleme, sonuç seçici işlevi gören bir Func<TFirst, TSecond, TResult> bağımsız değişkeni kabul eder. Sıkıştırılmış dizilerden yeni bir sonuç dizisi yansıtabilirsiniz.

foreach (string result in
    numbers.Zip(letters, (number, letter) => $"{number} = {letter} ({(int)letter})"))
{
    Console.WriteLine(result);
}
// This code produces the following output:
//     1 = A (65)
//     2 = B (66)
//     3 = C (67)
//     4 = D (68)
//     5 = E (69)
//     6 = F (70)

Yukarıdaki Zip aşırı yüklemeyle, belirtilen işlev ilgili öğelere numbers ve uygulanır ve lettersonuçların bir dizisini string oluşturur.

Select ile SelectMany

hem hem SelectMany de Select öğesinin çalışması, kaynak değerlerden bir sonuç değeri (veya değerler) üretmektir. Select her kaynak değer için bir sonuç değeri üretir. Bu nedenle genel sonuç, kaynak koleksiyonla aynı sayıda öğeye sahip bir koleksiyondur. Buna karşılık, SelectMany her kaynak değerden birleştirilmiş altcollections içeren tek bir genel sonuç üretir. bağımsız değişken SelectMany olarak geçirilen transform işlevinin her kaynak değer için numaralandırılabilir bir değer dizisi döndürmesi gerekir. SelectMany bu numaralandırılabilir dizileri birleştirerek büyük bir dizi oluşturur.

Aşağıdaki iki çizimde, bu iki yöntemin eylemleri arasındaki kavramsal fark gösterilmektedir. Her durumda selector (transform) işlevinin her kaynak değerden çiçek dizisini seçtiğini varsayalım.

Bu çizimde, kaynak koleksiyonla aynı sayıda öğeye sahip bir koleksiyonun nasıl Select döndürüldüğü gösterilmektedir.

Select() eylemini gösteren grafik

Bu çizimde, dizilerin ara dizisinin her ara dizideki her değeri içeren son bir sonuç değeriyle nasıl SelectMany birleştirildiği gösterilmektedir.

SelectMany() eylemini gösteren grafik

Kod örneği

Aşağıdaki örnek ve SelectManydavranışını Select karşılaştırır. Kod, kaynak koleksiyondaki her çiçek adı listesinden öğeleri alarak bir "buket" çiçek oluşturur. Aşağıdaki örnekte, dönüştürme işlevinin Select<TSource,TResult>(IEnumerable<TSource>, Func<TSource,TResult>) kullandığı "tek değer" bir değer koleksiyonudur. Bu örnek, her alt sorgudaki her dizeyi listelemek için ek foreach döngü gerektirir.

class Bouquet
{
    public required List<string> Flowers { get; init; }
}

static void SelectVsSelectMany()
{
    List<Bouquet> bouquets =
    [
        new Bouquet { Flowers = ["sunflower", "daisy", "daffodil", "larkspur"] },
        new Bouquet { Flowers = ["tulip", "rose", "orchid"] },
        new Bouquet { Flowers = ["gladiolis", "lily", "snapdragon", "aster", "protea"] },
        new Bouquet { Flowers = ["larkspur", "lilac", "iris", "dahlia"] }
    ];

    IEnumerable<List<string>> query1 = bouquets.Select(bq => bq.Flowers);

    IEnumerable<string> query2 = bouquets.SelectMany(bq => bq.Flowers);

    Console.WriteLine("Results by using Select():");
    // Note the extra foreach loop here.
    foreach (IEnumerable<string> collection in query1)
    {
        foreach (string item in collection)
        {
            Console.WriteLine(item);
        }
    }

    Console.WriteLine("\nResults by using SelectMany():");
    foreach (string item in query2)
    {
        Console.WriteLine(item);
    }
}

Ayrıca bkz.