Lambda ifadeleri, temsilciler ve olaylar

Tavsiye

Yazılım geliştirme konusunda yeni misiniz? İlk olarak Başlangıç öğreticileri ile başlayın. Lambda ifadelerini kullanmadan önce burada temel tür ve yöntem becerileri oluşturun.

Bazen küçük bir davranış parçasını, bir işlevi doğrudan başka bir yönteme geçirmek istersiniz. Örneğin, bir listeyi filtrelemek isteyebilirsiniz, ancak filtreleme koşulu duruma bağlı olarak değişir. Her olası koşul için ayrı bir adlandırılmış yöntem yazmak yerine koşulun kendisini bağımsız değişken olarak geçirirsiniz.

Lambda ifadeleri , bunu mümkün kılan C# özelliğidir. Lambda ifadesi, ad vermeden yazdığınız küçük, satır içi bir işlevdir. Parametre listesini gövdeden ayırmak için ok işlecini => kullanırsınız:

x => x * 2

Soldan sağa okuma: x giriş parametresidir, => "gider" anlamına gelir ve x * 2 gövdedir. Döndürülen değeri hesaplar. Parametre veya birden fazla parametre olmadığında, bunları parantez içinde sarmala: () => 42 veya (left, right) => left + right.

Temsilciler lambda ifadelerini destekler

Lambda ifadesi kullanmak için C# derleyicisinin iki şeyi bilmesi gerekir: parametre türleri ve dönüş türü. Bu açıklama, parametre türleri ve dönüş türü, temsilci türü olarak adlandırılır.

Temsilci türü, yöntem imzalarını temsil eden bir türdür. Bir temsilci türünün değişkeni, parametre türleri ve dönüş türü eşleşmesi sürece lambda ifadesi veya adlandırılmış yöntem gibi eşleşen herhangi bir yöntemi tutabilir.

Anahtar sözcüğüyle delegate bir temsilci türü bildirirsiniz:

delegate int Transform(int value);

Bu bildirim şunu söyler: "Transform , birini kabul int eden ve döndüren yöntemler için bir inttemsilci türüdür." Ardından, bu tür bir değişkene lambda ifadesi veya adlandırılmış bir yöntem atayabilirsiniz:

Transform doubler = x => x * 2;    // assign a lambda expression
Transform squarer = Square;         // assign a named method

Console.WriteLine(doubler(5));      // 10
Console.WriteLine(squarer(5));      // 25

static int Square(int value) => value * value;

Hem doubler hem de squarerTransform türünde bir değer tutar. Aynı normal yöntemlerde olduğu gibi çağırırsınız. Derleyici, yaptığınız atamaların bildirilen imzayla eşleşip eşleşmediğini doğrular.

Yerleşik temsilci türleri: Func ve Action

Her durum için özel bir temsilci türü bildirilmesi tekrarlanabilir. .NET, çoğu senaryoyu kapsayan Func ve Action olmak üzere iki genel temsilci türü ailesi sağlar, bu nedenle nadiren delegate anahtar sözcüğünü kendiniz kullanmanız gerekir.

Her iki aile de sıfır ile on altı giriş türü parametrelerine sahip sürümlerde gelir, bu nedenle herhangi bir sayıda girişe ölçeklendirilirler. İki aile arasındaki temel fark:

  • System.Func<T,TResult> (ve Func<T1, T2, TResult>vb.), değer döndüren bir yöntemi temsil eder. Son tür parametresi her zaman dönüş türüdür; öncekilerin tümü giriş türleridir.
  • System.Action<T> (ve Action<T1, T2>vb.), hiçbir şey döndürmeden (void) bir yöntemi temsil eder. Tüm tür parametreleri giriş türleridir. System.Action hiçbir tür parametresi olmadan girişi olmayan ve dönüş değeri olmayan bir yöntemi temsil eder.

Örneğin, Func<int, int, int> iki int girişi ve bir int sonucu olan bir yöntemi açıklar. Action<string> tek string girişli ve dönüş değeri olmayan bir yöntemi açıklar.

Func<int, int, int> add = (left, right) => left + right;
Action<string> report = message => Console.WriteLine($"Report: {message}");

int total = add(5, 9);
report($"5 + 9 = {total}");

Okuyucuların tam yöntem gövdesini taramadan amacı anlayabilmesi için lambdalarda açıklayıcı parametre adlarını kullanın.

Lambda ifadesini bir yönteme geçirme

Bir yöntem bir Func veya Action parametresi bildirdiğinde, çağıranlar bu temsilci türüyle eşleşen bir lambda ifadesi geçirir. Derleyici, lambda'nın parametre türlerinin ve dönüş türünün bildirilen temsilci türüyle eşleşip eşleşmediğini denetler. Eşleşmiyorsa kod derlenemez.

int[] numbers = [1, 2, 3, 4, 5, 6];
int[] evenNumbers = Filter(numbers, value => value % 2 == 0).ToArray();

Console.WriteLine(string.Join(", ", evenNumbers));

Filter yöntemi adlı Func<int, bool>bir predicate parametre bildirir. Türü Func<int, bool> , çağıranlara beklenen şekli bildirir: bir int giriş, bir bool sonuç. Çağıran, value => value % 2 == 0 öğesini bağımsız değişken olarak iletir. Bu düzen LINQ ve birçok .NET API'sinde görünür.

Lambda ifadelerini bağımsız tutma

Lambda ifadesi, çevresindeki koddan değişkenlere başvurabilir. Yakalama, lambda'nın kendi gövdesi dışında bildirilen bir değişkene referans tuttuğu anlamına gelir. Lambda ve yakaladığı değişkenlerin birleşimine kapanım adı verilir.

Herhangi bir şey yakalamanız gerekmeyen durumlarda değiştiriciyi static lambdaya ekleyin. Statik lambda yalnızca kendi parametrelerini ve gövdesi içinde bildirilen değerleri kullanabilir. Kapsayan kapsamdan yerel değişkenleri veya örnek durumunu yakalayamaz.

Func<int, bool> isEven = static value => value % 2 == 0;

Console.WriteLine(isEven(14));
Console.WriteLine(isEven(15));

Statik lambdalar amacı net hale getirir ve yanlışlıkla yakalamaları önler.

Girişler ilgisiz olduğunda atma parametrelerini kullanma

Bazen temsilci imzası ihtiyacınız olmayan parametreleri içerir. Bu seçimi açıkça belirtmek için "yok say" _ kullanın.

Yaygın örnekler arasında, sender veya EventArgs kullanmadığınız olay işleyicilerinin bulunduğu durumlar, sadece bazı girdilerin gerektiği geri çağırmalar ve kullanmadığınız bir dizin sağlayan LINQ aşırı yüklemeleri yer alır.

Action<int, int, string> statusUpdate = (_, _, message) => Console.WriteLine(message);
statusUpdate(200, 42, "Operation completed");

Göz ardı etmeler, hangi parametrelerin önemli olduğunu gösterdiği için okunabilirliği artırır.

Olaylar isteğe bağlı bildirimler sağlar

Olay, bir nesnenin (yayımcı) bir şey olduğunda diğer nesnelere (abonelere) bildirim göndermek için kullandığı bir mekanizmadır. Yayımcının kimin dinlediğini veya kaç abone olduğunu bilmesi gerekmez. Aboneler katılmayı tercih eder.

Olaylar temsilciler üzerinde oluşturulur. Olay, anahtar sözcüğü tarafından event uygulanan ek kısıtlamaları olan bir temsilci alanıdır: dış kod yalnızca olaya abone olabilir (+=) veya olay aboneliğini kaldırabilir (-=), yalnızca olayı bildiren sınıf çağırabilir (yükseltebilir).

Olay temsilcisi türleri için .NET kuralı System.EventHandler<TEventArgs>'dir; burada T bildirime dahil edilen veri türüdür. İmzasının her zaman iki parametresi vardır: sender (olayı tetikleyen nesne) ve türünde Tolay verileri.

MessagePublisher publisher = new();
publisher.MessagePublished += (_, message) => Console.WriteLine($"Received: {message}");

publisher.Publish("Records updated");

Kod incelemesi:

  • MessagePublisher event EventHandler<string>? MessagePublished bildirir. event anahtar sözcüğü, çağıranların yalnızca abone olabileceği veya aboneliği kaldırabileceği anlamına gelir; bunu doğrudan çağıramaz.
  • publisher.MessagePublished += (_, message) => ... bir lambda ifadesiyle abone olur. _ sender parametresini bu işleyici gerekli görmediği için göz ardı eder.
  • publisher.Publish("Records updated") olayı başlatır ve abone olunan her işleyiciyi çalıştırır.

Abone olun seçeneği isteğe bağlıdır. ?.Invoke(...) yöntemindePublish, olayın yalnızca en az bir abone eklendiğinde tetiklendiği anlamına gelir. Yayımcı, kimsenin dinleyip dinlemediğini bilmeden veya önemsemeden olayı yükseltir.

Ayrıca bakınız