Unity Eğitim
Dersler
Forum Sınav Merkezi Premium 💎
C# Dersleri

C# LINQ Giriş: Sorgu Sözdizimi ve Metot Zincirleme

Paylaşan: Unity Eğitim 07 October 2025 12 dakika okuma 256 görüntülenme

C# programlama dilinde veri sorgulama, özellikle büyük koleksiyonlar veya veritabanları ile çalışırken oldukça karmaşık ve tekrarlayıcı olabilir. İşte tam bu noktada **C# LINQ** (Language Integrated Query) devreye girerek, veri sorgulama işlemlerini çok daha okunabilir, tip güvenli ve verimli hale getirir. Bu makalede, **C# LINQ**'in temellerini, iki ana sözdizimi olan Sorgu Sözdizimi (Query Syntax) ve Metot Sözdizimi (Method Syntax) arasındaki farkları ve Unity/C# projelerinizde nasıl kullanabileceğinizi detaylı bir şekilde inceleyeceğiz.

Giriş: C# LINQ Nedir?

LINQ, .NET Framework 3.5 ile tanıtılan ve C# diline entegre edilmiş güçlü bir veri sorgulama özelliğidir. Amacı, farklı veri kaynaklarından (nesne koleksiyonları, XML, ilişkisel veritabanları, DataSet'ler vb.) veri sorgulamayı standart, tekdüze ve kolay okunabilir bir sözdizimi ile sağlamaktır. Artık farklı veri kaynakları için farklı sorgulama dilleri öğrenmek yerine, C# dilinin kendi yapısı içinde sorgular yazabilirsiniz. Bu sayede, kodun okunabilirliği artar, hatalar derleme zamanında yakalanır ve geliştirme süreci hızlanır.

LINQ'in Temel Faydaları

  • Okunabilirlik: SQL benzeri bir sözdizimi veya akıcı metot zincirleme ile sorgular çok daha anlaşılır hale gelir.
  • Tip Güvenliği: Sorgular derleme zamanında kontrol edilir, bu da çalışma zamanı hatalarını (örneğin yanlış sütun adı yazımı) büyük ölçüde azaltır.
  • Az Kod: Tekrarlayan döngüler ve koşullu ifadeler yazmak yerine, daha az kodla daha fazla iş yapmanızı sağlar.
  • Esneklik: Farklı veri kaynaklarına uygulanabilir.
  • Ertelemiş Yürütme (Deferred Execution): Sorgular, sonuçlarına ihtiyaç duyulana kadar yürütülmez, bu da performansı artırabilir.

LINQ Veri Kaynakları

**C# LINQ**, birçok farklı veri kaynağıyla çalışabilir:

  • LINQ to Objects: `List`, `Array`, `Dictionary` gibi bellek içi koleksiyonlar.
  • LINQ to SQL: SQL Server veritabanları.
  • LINQ to XML: XML belgeleri.
  • LINQ to Entities: Entity Framework aracılığıyla çeşitli veritabanları.

Unity projelerinde genellikle `LINQ to Objects` ile çalışırız; yani sahnedeki GameObject'ler, envanter sistemindeki öğeler veya oyuncu istatistikleri gibi bellek içi C# koleksiyonlarını sorgularız.

Sorgu Sözdizimi (Query Syntax)

Sorgu sözdizimi, SQL'e oldukça benzer bir yapıya sahiptir ve genellikle `from` anahtar kelimesiyle başlar. Bu sözdizimi, özellikle veritabanı geçmişi olan geliştiriciler için daha doğal gelebilir. Derleyici tarafından dahili olarak metot sözdizimine dönüştürülür.

Temel Bileşenler: `from`, `where`, `select`

Bir **C# LINQ** sorgusunun en temel bileşenleri `from`, `where` ve `select`'tir.


using System;
using System.Collections.Generic;
using System.Linq;

public class LINQQuerySyntaxExample
{
    public static void Main()
    {
        List<int> sayilar = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        // Çift sayıları seçen bir LINQ sorgusu
        var ciftSayilar = from sayi in sayilar
                          where sayi % 2 == 0
                          select sayi;

        Console.WriteLine("Çift Sayılar:");
        foreach (var s in ciftSayilar)
        {
            Console.WriteLine(s);
        }

        // Belirli bir uzunluktaki kelimeleri seçme
        List<string> kelimeler = new List<string> { "elma", "armut", "kiraz", "muz", "çilek" };
        var kisaKelimeler = from kelime in kelimeler
                            where kelime.Length < 5
                            select kelime.ToUpper(); // Seçilen kelimeleri büyük harfe çevir

        Console.WriteLine("\nKısa Kelimeler (Büyük Harf):");
        foreach (var k in kisaKelimeler)
        {
            Console.WriteLine(k);
        }
    }
}

Sıralama ve Gruplama: `orderby`, `group by`

`orderby` ile sonuçları sıralayabilir, `group by` ile belirli kriterlere göre gruplayabilirsiniz.


// ... önceki koddan devam ...

        List<string> meyveler = new List<string> { "Elma", "Armut", "Kiraz", "Muz", "Çilek", "Portakal" };

        // Uzunluğa göre sıralama
        var siraliMeyveler = from meyve in meyveler
                             orderby meyve.Length ascending, meyve descending // önce uzunluğa, sonra alfabetik tersine
                             select meyve;

        Console.WriteLine("\nSıralı Meyveler:");
        foreach (var m in siraliMeyveler)
        {
            Console.WriteLine(m);
        }

        // İlk harfe göre gruplama
        var gruplanmisMeyveler = from meyve in meyveler
                                 group meyve by meyve[0] into grup
                                 orderby grup.Key
                                 select new { IlkHarf = grup.Key, Meyveler = grup.ToList() };

        Console.WriteLine("\nGruplanmış Meyveler:");
        foreach (var grup in gruplanmisMeyveler)
        {
            Console.WriteLine($"İlk Harf: {grup.IlkHarf}");
            foreach (var m in grup.Meyveler)
            {
                Console.WriteLine($"  - {m}");
            }
        }

Birleştirme: `join`

İki farklı koleksiyonu belirli bir anahtar üzerinden birleştirmek için `join` kullanabilirsiniz.


        List<Oyuncu> oyuncular = new List<Oyuncu>
        {
            new Oyuncu { Id = 1, Ad = "Ali", Seviye = 10 },
            new Oyuncu { Id = 2, Ad = "Ayşe", Seviye = 15 },
            new Oyuncu { Id = 3, Ad = "Can", Seviye = 8 }
        };

        List<OyunEsyasi> esyalar = new List<OyunEsyasi>
        {
            new OyunEsyasi { SahipId = 1, EsyaAdi = "Kılıç" },
            new OyunEsyasi { SahipId = 2, EsyaAdi = "Kalkan" },
            new OyunEsyasi { SahipId = 1, EsyaAdi = "Zırh" }
        };

        var oyuncuEnvanter = from oyuncu in oyuncular
                             join esya in esyalar on oyuncu.Id equals esya.SahipId
                             select new { OyuncuAdi = oyuncu.Ad, Esya = esya.EsyaAdi };

        Console.WriteLine("\nOyuncu Envanterleri:");
        foreach (var item in oyuncuEnvanter)
        {
            Console.WriteLine($"{item.OyuncuAdi} - {item.Esya}");
        }
    }
}

public class Oyuncu { public int Id; public string Ad; public int Seviye; }
public class OyunEsyasi { public int SahipId; public string EsyaAdi; }

Metot Sözdizimi (Method Syntax / Extension Methods)

Metot sözdizimi, C# dilinin genişletme metotları (extension methods) özelliğini kullanarak sorguları zincirleme metot çağrıları şeklinde yazmamızı sağlar. Bu sözdizimi, daha esnek ve karmaşık sorgular için genellikle tercih edilir ve çoğu **C# LINQ** operatörünün bir karşılığı vardır.

Yaygın Metotlar: `Where`, `Select`, `OrderBy`

Sorgu sözdizimindeki `where`, `select`, `orderby` gibi ifadelerin metot karşılıkları da bulunur.


using System;
using System.Collections.Generic;
using System.Linq;

public class LINQMethodSyntaxExample
{
    public static void Main()
    {
        List<int> sayilar = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        // Çift sayıları seçen bir LINQ metot sorgusu
        var ciftSayilar = sayilar.Where(sayi => sayi % 2 == 0).ToList();

        Console.WriteLine("Metot ile Çift Sayılar:");
        foreach (var s in ciftSayilar)
        {
            Console.WriteLine(s);
        }

        List<string> kelimeler = new List<string> { "elma", "armut", "kiraz", "muz", "çilek" };
        var kisaKelimeler = kelimeler.Where(kelime => kelime.Length < 5)
                                     .Select(kelime => kelime.ToUpper())
                                     .OrderBy(kelime => kelime)
                                     .ToList();

        Console.WriteLine("\nMetot ile Kısa Kelimeler (Sıralı):");
        foreach (var k in kisaKelimeler)
        {
            Console.WriteLine(k);
        }
    }
}

Toplama Metotları: `Count`, `Sum`, `Average`

Koleksiyonlar üzerinde toplama işlemleri yapmak için özel metotlar bulunur.


// ... önceki koddan devam ...

        List<int> notlar = new List<int> { 75, 90, 60, 85, 95 };

        int toplamNot = notlar.Sum();
        double ortalamaNot = notlar.Average();
        int gecenOgrenciSayisi = notlar.Count(not => not >= 70);

        Console.WriteLine($"\nToplam Not: {toplamNot}");
        Console.WriteLine($"Ortalama Not: {ortalamaNot}");
        Console.WriteLine($"Geçen Öğrenci Sayısı: {gecenOgrenciSayisi}");

Tekil Eleman Seçimi: `First`, `FirstOrDefault`, `Single`, `SingleOrDefault`

Koleksiyondan tek bir eleman seçmek için kullanılırlar. `First()` ve `Single()` eşleşen eleman bulunamazsa veya `Single()` birden fazla eşleşen eleman bulursa hata fırlatırken, `FirstOrDefault()` ve `SingleOrDefault()` bu durumlarda varsayılan değeri (referans tipleri için `null`, değer tipleri için `0`) döndürür.


        List<string> isimler = new List<string> { "Ayşe", "Fatma", "Ali" };

        string ilkIsim = isimler.First(); // "Ayşe"
        string ali = isimler.FirstOrDefault(i => i == "Ali"); // "Ali"
        string deniz = isimler.FirstOrDefault(i => i == "Deniz"); // null

        Console.WriteLine($"\nİlk İsim: {ilkIsim}");
        Console.WriteLine($"Ali: {ali}");
        Console.WriteLine($"Deniz (varsa): {deniz ?? "Bulunamadı"}");
    }
}

Hangi Sözdizimini Ne Zaman Kullanmalı?

İki sözdizimi de aynı işi yapar ve derleyici tarafından birbirine dönüştürülür. Seçim genellikle kişisel tercihe veya projenin kodlama standartlarına bağlıdır:

  • Sorgu Sözdizimi: Daha karmaşık sorgular (özellikle `join`, `group by` gibi ifadeler içerenler) veya SQL geçmişi olanlar için daha okunabilir olabilir.
  • Metot Sözdizimi: Daha kısa, basit sorgular veya akıcı API'leri sevenler için idealdir. Özellikle tek bir `Where` veya `Select` işlemi yapılacaksa daha az kod gerektirir. Ayrıca, sorgu sözdiziminde bulunmayan bazı operatörler (örneğin `Any()`, `All()`, `Skip()`, `Take()`) sadece metot sözdiziminde mevcuttur.

Genellikle, bir sorgu `join` veya `group by` içeriyorsa sorgu sözdizimi, diğer durumlarda metot sözdizimi tercih edilebilir. Hatta her ikisini bir arada kullanmak da mümkündür (örneğin, sorgu sözdizimi ile başlayıp `.Where().Select()` gibi metotlarla devam etmek).

Pratik C# LINQ İpuçları

1. Ertelemiş Yürütme (Deferred Execution) ve `ToList()` Kullanımı

**C# LINQ** sorguları varsayılan olarak ertelemiş yürütme prensibiyle çalışır. Yani, sorguyu tanımladığınızda hemen çalışmaz; sadece sonuçlarına erişmeye çalıştığınızda (örneğin bir `foreach` döngüsü veya `ToList()`, `ToArray()` gibi metotlar çağrıldığında) çalışır. Bu, özellikle büyük veri kümeleriyle çalışırken performans avantajı sağlayabilir, çünkü sadece ihtiyaç duyulan veriyi işlersiniz. Ancak, aynı sorguyu birden çok kez çalıştırıyorsanız, her seferinde yeniden hesaplanacağı için performans kaybına neden olabilir. Bu durumda, sorgu sonucunu `ToList()` veya `ToArray()` ile bir koleksiyona atayarak sorguyu hemen çalıştırabilir ve sonuçları bellekte saklayabilirsiniz.


List<int> sayilar = new List<int> { 1, 2, 3, 4, 5 };
var ciftSayilar = sayilar.Where(s => s % 2 == 0); // Sorgu burada tanımlandı, henüz çalışmadı

sayilar.Add(6); // Kaynak liste değişti

// İlk foreach: 2, 4, 6 (6 da dahil olur çünkü sorgu burada çalışır)
foreach (var s in ciftSayilar)
{
    Console.WriteLine(s);
}

// Sorguyu hemen yürütmek için:
List<int> tekSayilarListesi = sayilar.Where(s => s % 2 != 0).ToList(); // Sorgu burada çalışır ve sonuç listeye atanır
sayilar.Add(7); // Kaynak liste değişti

// İkinci foreach: 1, 3, 5 (7 dahil olmaz çünkü liste ilk atandığında oluşturuldu)
foreach (var s in tekSayilarListesi)
{
    Console.WriteLine(s);
}

2. Anonim Tiplerle Esneklik

**C# LINQ** sorgularında, `select` ifadesi veya metodu ile yeni, anonim tipler oluşturabilirsiniz. Bu, mevcut tiplerin tamamını veya belirli özelliklerini seçerek sadece ihtiyacınız olan veriyi almanızı sağlar. Özellikle birden fazla koleksiyondan veri birleştirirken veya ara sonuçlar oluştururken çok kullanışlıdır.


List<Oyuncu> oyuncular = new List<Oyuncu>
{
    new Oyuncu { Id = 1, Ad = "Ali", Seviye = 10 },
    new Oyuncu { Id = 2, Ad = "Ayşe", Seviye = 15 }
};

var oyuncuBilgileri = oyuncular.Select(o => new { o.Ad, o.Seviye });

foreach (var bilgi in oyuncuBilgileri)
{
    Console.WriteLine($"Oyuncu Adı: {bilgi.Ad}, Seviye: {bilgi.Seviye}");
}

3. Performans için `Any()` ve `FirstOrDefault()`

Bir koleksiyonda belirli bir elemanın olup olmadığını kontrol etmek için `Where().Count() > 0` yerine `Any()` kullanmak daha verimlidir. `Any()`, koşulu sağlayan ilk elemanı bulduğunda aramayı durdurur ve `true` döndürür. Benzer şekilde, bir elemanı bulup bulmadığınızı kontrol etmek ve ilk elemanı almak için `Where().First()` veya `Where().FirstOrDefault()` kullanmak, tüm koleksiyonu filtreleyip yeni bir liste oluşturmaktan daha hızlıdır.


List<string> esyaListesi = new List<string> { "Kılıç", "Kalkan", "Zırh" };

// Kılıç var mı? (Verimli)
bool kilicVarMi = esyaListesi.Any(e => e == "Kılıç"); // true

// Kılıç var mı? (Daha az verimli, tüm listeyi tarar ve sayar)
bool kilicVarMiKotu = esyaListesi.Where(e => e == "Kılıç").Count() > 0; 

Console.WriteLine($"Kılıç var mı? {kilicVarMi}");

// İlk kalkanı bul (Verimli)
string ilkKalkan = esyaListesi.FirstOrDefault(e => e == "Kalkan"); // "Kalkan"
Console.WriteLine($"İlk Kalkan: {ilkKalkan ?? "Yok"}");

Yaygın Hatalar ve Çözümleri

Ertelemiş Yürütmeyi Yanlış Anlamak

Yukarıda bahsedildiği gibi, **C# LINQ** sorguları varsayılan olarak hemen çalışmaz. Bu durum, özellikle sorguyu tanımladıktan sonra veri kaynağını değiştirirseniz ve sorgunun eski veriler üzerinde çalışmasını bekliyorsanız kafa karışıklığına yol açabilir. Çözüm, sorgunun sonucunu hemen bir listeye veya diziye atamak için `ToList()` veya `ToArray()` kullanmaktır, böylece verinin o anki anlık görüntüsü alınmış olur.

`null` Değerlerle Çalışırken Dikkat

Sorgu sonucunda `null` olabilecek değerlerle çalışırken dikkatli olun. Örneğin, `FirstOrDefault()` size `null` dönebilir. Bu durumda, `null` kontrolü yapmadan bir özelliğine erişmeye çalışmak `NullReferenceException` hatasına yol açar. Güvenli navigasyon operatörü (`?.`) veya basit `if` kontrolleri kullanın.


List<Oyuncu> oyuncular = new List<Oyuncu>
{
    new Oyuncu { Id = 1, Ad = "Ali", Seviye = 10 }
};

Oyuncu bulunamayanOyuncu = oyuncular.FirstOrDefault(o => o.Ad == "Deniz");

// Hata riski: if (bulunamayanOyuncu.Seviye > 5) // NullReferenceException!

if (bulunamayanOyuncu != null)
{
    Console.WriteLine(bulunamayanOyuncu.Seviye);
}
else
{
    Console.WriteLine("Oyuncu bulunamadı.");
}

// Veya C# 6.0 sonrası güvenli navigasyon:
int? seviye = bulunamayanOyuncu?.Seviye; // Eğer bulunamayanOyuncu null ise seviye de null olur
Console.WriteLine(seviye.HasValue ? seviye.ToString() : "Seviye yok.");

Karmaşık Sorgularda Okunabilirlik

Çok sayıda `Where`, `Select`, `OrderBy` gibi metodu zincirlediğinizde sorgular okunaksız hale gelebilir. Bu durumda, sorguyu daha küçük, mantıksal parçalara ayırmak veya sorgu sözdizimine geçmek okunabilirliği artırabilir. Ayrıca, ara sonuçları açıklayıcı değişken adlarına atamak da faydalıdır.

Performans ve Optimizasyon Notları

  • Gereksiz Sorgulardan Kaçının: Aynı veriyi defalarca sorgulamak yerine, sonucu bir kez alıp bellekte tutun (eğer veri sık değişmiyorsa).
  • Doğru Metodu Kullanın: Sadece bir elemanın varlığını kontrol etmek için `Any()` kullanın, `Count() > 0` kullanmayın. İlk elemanı almak için `First()`/`FirstOrDefault()` kullanın, tüm listeyi filtreleyip sonra `First()` çağırmayın.
  • Veri Kaynağını Bilin: Eğer veritabanı gibi dış bir kaynağı sorguluyorsanız (`IQueryable`), sorgunuzun SQL'e nasıl çevrildiğini anlamak önemlidir. Gereksiz veya pahalı işlemleri bellek içi işleme (`IEnumerable`) bırakmak yerine veritabanında yapmaya çalışın. (Unity'de genellikle `LINQ to Objects` kullandığımız için bu nokta daha az kritik olabilir, ancak büyük veri kümeleriyle çalışırken yine de akılda bulundurulmalıdır.)
  • Sorguları Basit Tutun: Çok karmaşık tek bir sorgu yerine, birden fazla daha basit sorguyu birleştirmeyi düşünün.

Unity ve C# projelerinizde **C# LINQ**'i etkin bir şekilde kullanarak veri manipülasyonu ve sorgulama işlemlerini çok daha verimli ve keyifli hale getirebilirsiniz. Unutmayın, pratik yapmak ve farklı senaryolarda denemeler yapmak, LINQ'in gücünü tam olarak kavramanıza yardımcı olacaktır.

🧠 Ders Sonu Değerlendirme Testi

Dersi tamamladıktan sonra bilgilerinizi test edin ve ekstra puanlar kazanın.

🔥 +50 XP Ödül
🔒

Sınava Katılmak İçin Giriş Yapın

Bu ders sonu testini çözebilmek, bilginizi test edip **+50 XP** kazanmak ve **Sınav Şampiyonu** rozetinin kilidini açmak için üye girişi yapmalısınız.

Yorumlar (0)

Yorum yazabilmek ve derslere katkıda bulunabilmek için giriş yapmalısınız.

İlk yorumu siz yapın!