Aracılığıyla paylaş


System.Delegate ve delegate anahtar sözcüğü

Önceki

Bu makale, .NET'te temsilcileri destekleyen sınıfları ve bu sınıfların anahtar sözcükle nasıl eşlendiğini delegate kapsar.

Temsilciler nedir?

Temsilciyi, bir nesneye referans kaydetmek gibi, bir yönteme referans kaydetmenin bir yolu olarak düşünün. Nesneleri metotlara geçirebildiğiniz gibi, temsilciler kullanarak metot başvurularını da geçirebilirsiniz. Bu, farklı davranışlar sağlamak için farklı yöntemlerin "takılı" olabileceği esnek kod yazmak istediğinizde kullanışlıdır.

Örneğin, iki sayı üzerinde işlem gerçekleştirebilen bir hesap makineniz olduğunu düşünün. Toplama, çıkarma, çarpma ve bölmeyi ayrı yöntemlere sabit kodlamak yerine, iki sayı alan ve sonuç döndüren herhangi bir işlemi temsil etmek için temsilcileri kullanabilirsiniz.

Temsilci türlerini tanımlama

Şimdi anahtar sözcüğünü kullanarak delegate temsilci türleri oluşturmayı görelim. Bir temsilci türü tanımladığınızda, temelde bu temsilcide ne tür yöntemlerin depolanabileceğini açıklayan bir şablon oluşturursunuz.

Bir yöntem imzasına benzeyen ancak başında anahtar sözcüğü olan söz dizimini delegate kullanarak bir temsilci türü tanımlarsınız:

// Define a simple delegate that can point to methods taking two integers and returning an integer
public delegate int Calculator(int x, int y);

Bu Calculator temsilci, iki int parametre alan ve bir döndüren herhangi bir intyönteme başvuruları tutabilir.

Şimdi daha pratik bir örneğe bakalım. Bir listeyi sıralamak istediğinizde, sıralama algoritmasına öğeleri nasıl karşılaştırabileceğinizi söylemeniz gerekir. Şimdi temsilcilerin List.Sort() yöntemine nasıl yardımcı olduğunu görelim. İlk adım, karşılaştırma işlemi için bir temsilci türü oluşturmaktır:

// From the .NET Core library
public delegate int Comparison<in T>(T left, T right);

Bu Comparison<T> temsilci, aşağıdaki özelliklere sahip herhangi bir yöntemin referanslarını tutabilir:

  • Türünde iki parametre alır T
  • "Küçüktür", "eşittir" veya "büyüktür" ifadesini belirtmek için bir int (genellikle -1, 0 veya 1) döndürür

Bunun gibi bir temsilci türü tanımladığınızda, derleyici otomatik olarak imzanızla eşleşen bir System.Delegate sınıf oluşturur. Bu sınıf, sizin için yöntem başvurularını depolamanın ve çağırmanın tüm karmaşıklığını işler.

Temsilci Comparison türü genel bir türdür ve herhangi bir türle Tçalışabileceği anlamına gelir. Geneller hakkında daha fazla bilgi için bkz. Genel sınıflar ve yöntemler.

Söz dizimi değişken bildirmeye benzer görünse de, aslında yeni bir tür bildirdiğinize dikkat edin. Temsilci türlerini sınıfların içinde, doğrudan ad alanlarının içinde, hatta genel ad alanında tanımlayabilirsiniz.

Uyarı

Temsilci türlerinin (veya diğer türlerin) doğrudan genel ad alanında bildirilmesi önerilmez.

Derleyici ayrıca bu yeni tür için ek ve kaldır işleyicileri oluşturur, böylece bu sınıfın istemcileri bir örneğin çağrı listesinden yöntem ekleyebilir ve kaldırabilir. Derleyici, eklenen veya kaldırılan yöntemin imzasının temsilci türü bildirildiğinde kullanılan imzayla eşleşmesini zorlar.

Temsilci örneklerini tanımlama

Temsilci türünü tanımladıktan sonra, bu türde örnekler (değişkenler) oluşturabilirsiniz. Bunu, bir metoda referans depolayabileceğiniz bir "yuva" olarak düşünün.

C# içindeki tüm değişkenler gibi, temsilci örneklerini doğrudan bir ad alanında veya genel ad alanında bildiremezsiniz.

// Inside a class definition:
public Comparison<T> comparator;

Bu değişkenin türü ( Comparison<T> daha önce tanımladığınız temsilci türü) ve değişkenin adı şeklindedir comparator. Bu noktada, comparator henüz herhangi bir metota işaret etmez; doldurulmayı bekleyen boş bir slot gibidir.

Temsilci değişkenlerini, diğer değişken türlerinde olduğu gibi yerel değişkenler veya yöntem parametreleri olarak da bildirebilirsiniz.

Temsilcileri çağırma

Bir yöntemi işaret eden bir temsilci örneğiniz olduğunda, bu yöntemi temsilci aracılığıyla çalıştırabilirsiniz. Bir temsilcinin çağrı listesinde yer alan yöntemleri, bu temsilciyi bir yöntem gibi çağırarak çalıştırırsınız.

Yöntemi, Sort() nesnelerin sırasını belirlemek için karşılaştırma temsilcisini şu şekilde kullanır:

int result = comparator(left, right);

Bu satırda, kod temsilciye eklenmiş olan yöntemi çağırır. Temsilci değişkenini bir yöntem adıymış gibi ele alır ve normal yöntem çağrısı söz dizimini kullanarak çağırırsınız.

Ancak, bu kod satırı güvenli olmayan bir varsayımda bulunur: temsilciye bir hedef yöntemin eklendiğini varsayar. Hiçbir yöntem eklenmemişse, yukarıdaki satır bir NullReferenceException atılmasına neden olur. Bu sorunu gidermek için kullanılan desenler basit bir null denetiminden daha karmaşıktır ve bu serinin ilerleyen bölümlerinde ele alınmıştır.

Çağırma hedeflerini atama, ekleme ve kaldırma

Artık temsilci türlerini tanımlamayı, temsilci örneklerini bildirmeyi ve temsilcileri çağırmayı biliyorsunuz. Peki bir yöntemi temsilciye nasıl bağlarsınız? Temsilci ataması burada devreye girer.

Temsilci kullanmak için, buna bir yöntem atamanız gerekir. Atadığınız yöntem, temsilci türünün tanımladığı aynı imzaya (aynı parametrelere ve dönüş türüne) sahip olmalıdır.

Şimdi pratik bir örnek görelim. Dizelerin listesini uzunluklarına göre sıralamak istediğinizi varsayalım. Temsilci imzası ile eşleşen Comparison<string> bir karşılaştırma yöntemi oluşturmanız gerekir:

private static int CompareLength(string left, string right) =>
    left.Length.CompareTo(right.Length);

Bu yöntem iki dize alır ve hangi dizenin "daha büyük" (bu durumda daha uzun) olduğunu belirten bir tamsayı döndürür. Metot private olarak tanımlanır ve bu gayet iyi. Bir temsilciyle kullanmak için yöntemin ortak arabiriminizin parçası olması gerekmez.

Şimdi bu yöntemi List.Sort() yöntemine iletebilirsiniz.

phrases.Sort(CompareLength);

Yöntem adını parantez olmadan kullandığınıza dikkat edin. Bu, derleyiciye yöntem başvuruyu daha sonra çağrılabilecek bir temsilciye dönüştürmesini söyler. Sort() yöntemi, iki dizeyi karşılaştırması gerektiğinde sizin CompareLength yönteminizi çağıracaktır.

Ayrıca bir temsilci değişkeni bildirerek ve yöntemini ona atayarak da daha açık olabilirsiniz:

Comparison<string> comparer = CompareLength;
phrases.Sort(comparer);

Her iki yaklaşım da aynı şeyi gerçekleştirir. İlk yaklaşım daha kısa, ikincisi ise temsilci atamasını daha açık hale getirir.

Basit yöntemler için, ayrı bir yöntem tanımlamak yerine lambda ifadelerinin kullanılması yaygındır:

Comparison<string> comparer = (left, right) => left.Length.CompareTo(right.Length);
phrases.Sort(comparer);

Lambda ifadeleri, satır içi basit yöntemleri tanımlamak için küçük bir yol sağlar. Temsilci hedefleri için lambda ifadelerinin kullanılması , sonraki bir bölümde daha ayrıntılı olarak ele alınmıştır.

Şimdiye kadarki örneklerde tek bir hedef yöntemi olan temsilciler gösterilmektedir. Ancak, temsilci nesneleri tek bir temsilci nesnesine bağlı birden çok hedef yöntemi olan çağırma listelerini destekleyebilir. Bu özellik özellikle olay işleme senaryoları için kullanışlıdır.

Temsilci ve Çok Yönlü Yayın Temsilcisi sınıfları

Arka planda, kullanmakta olduğunuz temsilci özellikleri .NET framework'te iki temel sınıf üzerinde oluşturulur: Delegate ve MulticastDelegate. Genellikle bu sınıflarla doğrudan çalışmazsınız, ancak bunlar temsilcilerin çalışmasını sağlayan temeli sağlar.

System.Delegate sınıfı ve doğrudan alt sınıfı System.MulticastDelegate temsilci oluşturma, yöntemleri temsilci hedefleri olarak kaydetme ve temsilciyle kaydedilen tüm yöntemleri çağırma için çerçeve desteği sağlar.

İşte ilginç bir tasarım ayrıntısı: System.Delegate ve System.MulticastDelegate kullanabileceğiniz temsilci türleri bunlar değildir. Bunun yerine, oluşturduğunuz tüm belirli temsilci türleri için temel sınıflar olarak görev yapar. C# dili bu sınıflardan doğrudan devralmanızı engeller; bunun yerine anahtar sözcüğünü delegate kullanmanız gerekir.

Bir temsilci türü bildirmek için delegate anahtar sözcüğünü kullandığınızda, C# derleyicisi otomatik olarak sizin belirttiğiniz imzayla türetilmiş bir MulticastDelegate sınıfı oluşturur.

Neden bu tasarım?

Bu tasarımın kökleri C# ve .NET'in ilk sürümündedir. Tasarım ekibinin birkaç hedefi vardı:

  1. Tür Güvenliği: Ekip, temsilcileri kullanırken dilin tür güvenliğini zorunlu kıldığından emin olmak istedi. Bu, temsilcilerin doğru tür ve sayıda bağımsız değişkenle çağrıldığına ve dönüş türlerinin derleme zamanında doğru şekilde kontrol edildiğine emin olmak anlamına gelir.

  2. Performans: Derleyicinin belirli yöntem imzalarını temsil eden somut temsilci sınıfları oluşturmasını sağlayarak, çalışma zamanı temsilci çağrılarını iyileştirebilir.

  3. Basitlik: Temsilciler, genel kullanıma sunulmasından önceki 1.0 .NET sürümüne dahil edildi. Tasarım, zamanın kısıtlamaları içinde çalışmak için gerekli.

Çözüm, derleyicinin yöntem imzalarınızla eşleşen somut temsilci sınıflarını oluşturmasını sağlamak ve karmaşıklığı sizden gizleyerek tür güvenliğini sağlamaktı.

Temsilci yöntemleriyle çalışma

Türetilmiş sınıfları doğrudan oluşturamasanız da, bazen Delegate ve MulticastDelegate sınıflarında tanımlanan yöntemleri kullanırsınız. Hakkında bilinmesi gereken en önemliler şunlardır:

Birlikte çalıştığınız her temsilci MulticastDelegate sınıfından türetilmiştir. "Çok noktaya yayın" temsilcisi, bir temsilci aracılığıyla çağrılırken birden fazla yöntem hedefinin çağrılabileceği anlamına gelir. Özgün tasarım, yalnızca bir yöntemi çağırabilen temsilciler ile birden çok yöntemi çağırabilen temsilciler arasında ayrım yapmayı dikkate alır. Uygulamada, bu ayrım başlangıçtakinden daha az yararlı olduğu kanıtlanmıştır, bu nedenle .NET'teki tüm temsilciler birden çok hedef yöntemi destekler.

Temsilcilerle çalışırken en sık kullanılan yöntemler şunlardır:

  • Invoke(): Temsilciye eklenen tüm yöntemleri çağırır
  • BeginInvoke() / EndInvoke(): Zaman uyumsuz çağırma desenleri için kullanılır (ancak async/await artık tercih edilir)

Çoğu durumda, bu yöntemleri doğrudan çağırmazsınız. Bunun yerine, yukarıdaki örneklerde gösterildiği gibi temsilci değişkeninde yöntem çağrısı söz dizimini kullanacaksınız. Ancak , bu serinin devamında göreceğiniz gibi, doğrudan bu yöntemlerle çalışan desenler vardır.

Özet

C# dilinin söz diziminin temel alınan .NET sınıflarıyla nasıl eşlendiğini gördüğünüze göre, daha karmaşık senaryolarda güçlü bir şekilde yazılan temsilcilerin ne kadar kullanıldığını, oluşturulduğunu ve çağrıldığını keşfetmeye hazırsınız.

İleri