29 Ekim 2013 Salı

C# Lambda İfadeleri

Lambda ifadesi; tesmsilciler veya ifade ağacı türlerini oluşturmak için kullanabileceğiniz anonim bir işlevdir. Lambda ifadeleri kullanarak, bağımsız değişken yerine geçebilecek veya işlev çağrılarının değeri olarak döndürülebilecek, yerel işlevler yazabilirsiniz. Lambda ifadeleri LINQ sorgu ifadeleri yazmak için özellikle yararlıdır.
Lambda ifadesi oluşturmak için, => lambda işlecinin sol tarafında giriş parametreleri belirtin ve ifadeyi veya deyim bloğunu diğer tarafa yerleştirin. Örneğin, x => x * xlambda ifadesi, x adlı bir parametre belirtir ve x değerinin karesini döndürür. Bu ifadeyi aşağıdaki örnekte gösterildiği gibi bir temsilci türüne atayabilirsiniz:
delegate int del(int i);
static void Main(string[] args)
{
    del myDelegate = x => x * x;
    int j = myDelegate(5); //j = 25
}
Bir ifade ağacı türü oluşturmak için:
using System.Linq.Expressions;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Expression<del> myET = x => x * x;
        }
    }
}
=> işleci atamayla aynı önceliğe sahiptir (=) ve sağa ilişkilendirilmiştir.
Lambda ifadeleri yöntem tabanlı LINQ sorgularında standart sorgu işleci yöntemlerinin bağımsız değişkenleri olarak kullanılır, örneğin Where.
Where yöntemini Enumerable sınıfının içinde çağırmak için yöntem tabanlı sözdizimi kullandığınızda (Nesneler için LINQ ve LINQ to XML durumlarında olduğu gibi) parametre, temsilci türüdür System.Func<T, TResult>. Lambda ifadesi, bu temsilciyi oluşturmak için en kullanışlı yoldur. Aynı yöntemi örneğin System.Linq.Queryable sınıfında çağırırsanız (LINQ to SQL içinde yaptığınız gibi) parametre türü, en fazla on altı giriş parametresi bildiren bir System.Linq.Expressions.Expression<Func> olur. Yine, Lambda ifadesi bu ifade ağacını oluşturmanın yalnızca çok kısa bir yoludur. Aslında lambdadan oluşturulan nesnenin türü farklı olsa da, lambdalar Where çağrılarının benzer görünmesine izin verir.
Önceki örnekte, temsilci imzasında örtük olarak yazılmış ve int türünde bir giriş parametresi olduğuna ve bir int döndürdüğüne dikkat edin. Lambda ifadesi bu türden bir temsilciye dönüştürülebilir çünkü ayrıca bir giriş parametresi (x) ve derleyicinin dolaylı olarak int türüne çevirebildiği bir dönüş değerine sahiptir. (Tür çıkarımı, ilerleyen bölümlerde daha ayrıntılı bir şekilde tartışılmıştır.) Temsilci, giriş parametresi 5 kullanılarak çağrıldığında 25 sonucunu döndürür.
Lambda ifadelerinin is veya as işlecinin sol tarafında olmasına izin verilmez.
Anonim yöntemler için uygulanan tüm kısıtlamalar lambda ifadeleri için de geçerlidir. Daha fazla bilgi için bkz. Anonim Yöntemler (C# Programlama Kılavuzu).
Lambda İfadeleri
Sağ taraftaki ifadeye sahip bir lambda ifadesi, expression lambda olarak adlandırılır. İfade lambdaları İfade Ağaçları (C# ve Visual Basic) oluşturulurken sıkça kullanılır. Bir lambda ifadesi, ifadenin sonucunu verir ve aşağıdaki temel biçimi alır:
(input parameters) => expression
Parantezler yalnızca lambdanın bir çıktı parametresi varsa isteğe bağlıdır; aksi takdirde bunlar gereklidir. İki veya daha fazla giriş parametresi, ayraç içinde virgülle ayrılır:
(x, y) => x == y
Bazen derleyicinin giriş türlerini çıkarması zor veya imkansız olabilir. Bu durumda türleri aşağıdaki örnekte olduğu gibi açıkça belirtebilirsiniz:
(int x, string s) => s.Length > x
Boş ayraçlarla sıfır giriş parametrelerini belirtin:
() => SomeMethod()
Önceki örnekte bir lambda ifadesi gövdesinin yöntem çağrısından oluşabileceğini unutmayın. Ancak SQL Server gibi başka bir etki alanında kullanılmak üzere ifade ağaçları oluşturuyorsanız lambda ifadelerinde çağıran yöntemi kullanmanız gerekir. Yöntemler .NET ortak dil çalışma zamanı bağlamının dışında anlamlı olmayacaktır.
Deyim Lambda
Ayraçlar arasındaki deyimler hariç statement lambda, expression lambda'ya benzer:
(input parameters) => {statement;}
Bir lambda deyiminin gövdesi herhangi bir sayıda deyimden oluşabilir; ancak, uygulamada genellikle iki veya üçten fazla değildir.
delegate void TestDelegate(string s);
…
TestDelegate myDel = n => { string s = n + " " + "World"; Console.WriteLine(s); };
myDel("Hello");
Anonim yöntemler gibi deyim lambdaları da ifade ağacı oluşturmak için kullanılamaz.
Zaman Uyumsuz Lambda 
async ve await anahtar sözcüklerini kullanarak zaman uyumsuz işleme içeren lambda ifadeleri ve deyimlerini kolayca oluşturabilirsiniz. Örneğin, aşağıdaki Windows Forms örneği, ExampleMethodAsync zaman uyumsuz yöntemini çağıran ve bekleyen bir olay işleyici içerir.
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        // ExampleMethodAsync returns a Task.
        await ExampleMethodAsync();
        textBox1.Text += "\r\nControl returned to Click event handler.\r\n";
    }

    async Task ExampleMethodAsync()
    {
        // The following line simulates a task-returning asynchronous process.
        await Task.Delay(1000);
    }
}
Zaman uyumsuz lambda kullanarak aynı olay işleyicisini ekleyebilirsiniz. Bu işleyiciyi eklemek için aşağıdaki örnekte göründüğü gibi lambda parametre listesinden önce birasync değiştirici ekleyin.
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        button1.Click += async (sender, e) =>
        {
            // ExampleMethodAsync returns a Task.
            await ExampleMethodAsync();
            textBox1.Text += "\r\nControl returned to Click event handler.\r\n";
        };
    }

    async Task ExampleMethodAsync()
    {
        // The following line simulates a task-returning asynchronous process.
        await Task.Delay(1000);
    }
}
Zaman uyumsuz yöntemlerin nasıl oluşturulacağı ve kullanılacağı hakkında daha fazla bilgi için bkz. Async ve Await ile Zaman Uyumsuz Programlama (C# ve Visual Basic).
Standart Sorgu İşlevleriyle Lambda İfadeleri
Standard sorgu işleçlerinin çoğu, türü Func<T, TResult> genel temsilci ailesinden olan bir giriş parametresine sahiptir. Func<T, TResult> kullanım türü parametrelerini, girdi parametrelerinin sayısı ve türü ile temsilcinin dönüş türünü tanımlamak üzere devreder. Func temsilcileri, bir kaynak veri kümesindeki her öğeye uygulanan kullanıcı tanımlı ifadelerin kapsüllenmesi için son derece kullanışlıdır. Örneğin, aşağıdaki temsilci türünü göz önünde bulundurun:
public delegate TResult Func<TArg0, TResult>(TArg0 arg0)
Temsilci; int değerinin giriş parametresi, bool değerinin dönüş değeri olduğu yerde Func<int,bool> myFunc olarak oluşturulabilir. Dönüş değeri her zaman son tür parametresinde belirtilir. Func<int, string, bool> bir temsilci ile int ve string olmak üzere iki giriş parametresi ve bool dönüş türü tanımlar. Aşağıdaki Func temsilcisi çağrıldığında, giriş parametresinin 5'e eşit olup olmadığını belirtmek için true ya da false döndürür:
Func<int, bool> myFunc = x => x == 5;
bool result = myFunc(4); // returns false of course
Bağımsız değişken türü Expression<Func> olduğunda, örneğin standart sorgu işleçlerinde tanımlı System.Linq.Queryable gibi, lambda ifadesi sağlayabilirsiniz.Expression<Func> bağımsız değişken belirlediğinizde lambda ifade ağacında derlenecek.
Standart sorgu işleci olan Count yöntemi burada gösterilmektedir:
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count(n => n % 2 == 1);
Derleyici giriş parametresinin türünü çıkarabilir veya bunu açıkça belirtebilirsiniz. Bu belirli lambda ifadesi, ikiye bölündüğünde 1 kalan veren tamsayıları (n) sayar.
Aşağıdaki yöntem, numbers dizisinde 9'un sol tarafındaki tüm öğeleri içeren bir sıra oluşturur çünkü bu sayı sıradaki şu koşulu yerine getirmeyen ilk sayıdır:
var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6);
Bu örnek birden fazla giriş parametrelerini paranteze alarak belirtmeyi gösterir. Yöntem, değeri konumundan daha az olan bir sayı ile karşılaşana dek sayı dizisindeki tüm öğeleri döndürür. Lambda işlecini (=>) büyük veya eşittir işleciyle (>=) karıştırmayın.
var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);

Lambda İçinde Tür Çıkarımı

Lambda yazarken genelde giriş parametreleri için bir tür belirtmeniz gerekmez, derleyici lambda gövdesine, temel temsilci türüne ve C# Dil Belirtiminde açıklanan diğer etkenlere göre türü belirleyebilir. Standart sorgu işleçlerinin çoğunda ilk giriş kaynak dizisindeki öğelerin türüdür. IEnumerable<Customer> sorguluyorsanız giriş değişkeni, buna ilişkin yöntemlere ve özelliklere erişiminiz olduğu anlamına gelen Customer nesnesi olarak algılanır:
customers.Where(c => c.City == "London");
Lambdalar için genel kurallar şunlardır:
  • Lambda temsilci türüyle aynı sayıda parametre içermelidir.
  • Lambdadaki her giriş parametresi, denk gelen temsilci parametresine dolaylı olarak dönüştürülebilir olmalıdır.
  • Lambdanın (varsa) dönüş değeri örtük olarak temsilcinin dönüş türüne dönüştürülebilir olmalıdır.
Ortak tür sisteminin "lambda ifadesi" için iç kavramı olmadığından kendi içlerindeki lambda ifadelerinin türü olmadığını unutmayın. Ancak bazen bir lambda ifadesinin "türünden" resmi olmayan bir şekilde bahsetmek daha kolaydır. Bu gibi durumlarda, tür lambda ifadesinin dönüştürüldüğü temsilci türüne veya Expression türüne başvurur.
Lambda İfadelerinde Değişken Kapsamı
Lambda ifadeleri, kapsayan yöntemin kapsamında olan veya lambdanın tanımlandığı tür içinde harici değişkenlere başvurabilir. Bu şekilde yakalanan değişkenler, diğer türlü kapsam dışına çıkacak ve çöp olarak toplanacak olsa dahi lambda ifadesinde kullanılmak üzere saklanır. Bir lambda ifadesinde tüketilebilmesi için öncelikle mutlaka bir harici değişken tayin edilmelidir. Aşağıdaki örnek bu kuralları gösterir:
delegate bool D();
delegate bool D2(int i);

class Test
{
    D del;
    D2 del2;
    public void TestMethod(int input)
    {
        int j = 0;
        // Initialize the delegates with lambda expressions.
        // Note access to 2 outer variables.
        // del will be invoked within this method.
        del = () => { j = 10;  return j > input; };

        // del2 will be invoked after TestMethod goes out of scope.
        del2 = (x) => {return x == j; };
      
        // Demonstrate value of j:
        // Output: j = 0 
        // The delegate has not been invoked yet.
        Console.WriteLine("j = {0}", j);        // Invoke the delegate.
        bool boolResult = del();

        // Output: j = 10 b = True
        Console.WriteLine("j = {0}. b = {1}", j, boolResult);
    }

    static void Main()
    {
        Test test = new Test();
        test.TestMethod(5);

        // Prove that del2 still has a copy of
        // local variable j from TestMethod.
        bool result = test.del2(10);

        // Output: True
        Console.WriteLine(result);
           
        Console.ReadKey();
    }
}

Lambda ifadelerindeki değişken kapsam için aşağıdaki kurallar geçerlidir:
  • Yakalanan bir değer, buna başvuran temsilci kapsamın dışına çıkıncaya kadar çöpte toplanamaz.
  • Bir lambda ifadesi içinde tanıtılan değişkenler, dış yöntemde görünmez.
  • Lambda ifadesi, kapsayan bir yöntemden alınan ref veya out parametresini doğrudan yakalayamaz.
  • Lambda ifadesindeki bir dönüş ifadesi, kapsayan yöntemin döndürülmesine neden olmaz.
  • Lambda ifadesi; hedefi, gövdenin dışında olan veya kapsanan anonim bir işlevin gövdesinde olan gotobreak veya continue deyimini içeremez.

Hiç yorum yok:

Yorum Gönder