Aracılığıyla paylaş


21 Temsilci

21.1 Genel

Temsilci bildirimi, sınıfından türetilen bir sınıfı System.Delegatetanımlar. Temsilci örneği, her biri çağrılabilen varlık olarak adlandırılan bir veya daha fazla yöntemin listesi olan çağrı listesini kapsüller. Örneğin yöntemler, çağrılabilen bir varlık bir örnekten ve bu örnekteki bir yöntemden oluşur. Statik yöntemler için çağrılabilen varlık yalnızca bir yöntemden oluşur. Uygun bir bağımsız değişken kümesiyle bir temsilci örneği çağırmak, temsilcinin çağrılabilir varlıklarının her birinin belirtilen bağımsız değişkenler kümesiyle çağrılmalarına neden olur.

Not: Temsilci örneğinin ilginç ve kullanışlı bir özelliği, kapsüllediği yöntemlerin sınıflarını bilmemesi veya önemsememesidir; önemli olan tek şey, bu yöntemlerin temsilcinin türüyle uyumlu (§21.4) olmasıdır. Bu, temsilcileri "anonim" çağırma için mükemmel bir şekilde uygun hale getirir. son not

21.2 Temsilci bildirimleri

delegate_declaration, yeni bir temsilci türü bildiren bir type_declaration (§14.7) olur.

delegate_declaration
    : attributes? delegate_modifier* 'delegate' return_type delegate_header
    | attributes? delegate_modifier* 'delegate' ref_kind ref_return_type
      delegate_header
    ;

delegate_header
    : identifier '(' parameter_list? ')' ';'
    | identifier variant_type_parameter_list '(' parameter_list? ')'
      type_parameter_constraints_clause* ';'
    ;
    
delegate_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | unsafe_modifier   // unsafe code support
    ;

unsafe_modifier, §24.2'de tanımlanır.

Aynı değiştiricinin temsilci bildiriminde birden çok kez görünmesi derleme zamanı hatasıdır.

variant_type_parameter_list sağlayan temsilci bildirimi genel bir temsilci bildirimidir. Ayrıca, genel bir sınıf bildiriminin veya genel yapı bildiriminin içinde iç içe yerleştirilmiş tüm temsilciler kendi genel bir temsilci bildirimidir, çünkü içeren tür için tür bağımsız değişkenleri oluşturulmuş bir tür (§8.4) oluşturmak için sağlanmalıdır.

new Değiştiriciye yalnızca başka bir tür içinde bildirilen temsilcilerde izin verilir; bu durumda, bu tür bir temsilcinin devralınan bir üyeyi §15.3.5'te açıklandığı gibi aynı ada göre gizlediğini belirtir.

public, protected, internalve private değiştiricileri temsilci türünün erişilebilirliğini denetler. Temsilci bildiriminin gerçekleştiği bağlama bağlı olarak, bu değiştiricilerden bazılarına izin verilmeyebilir (§7.5.2).

Temsilcinin tür adı tanımlayıcıdır.

Yöntemlerde (§15.6.1) olduğu gibi, varsaref, temsilci başv döndürür; aksi takdirde, return_typetemsilci değer döndürmez; aksi takdirde temsilci değere göre döndürür.

İsteğe bağlı parameter_list temsilcinin parametrelerini belirtir.

Değere göre döndürür veya değer döndürmez temsilci bildiriminin return_type , varsa, temsilci tarafından döndürülen sonucun türünü belirtir.

Başv temsilci bildiriminin ref_return_type, temsilci tarafından döndürülen variable_reference (§9.5) tarafından başvuruda bulunan değişkenin türünü belirtir.

İsteğe bağlı variant_type_parameter_list (§19.2.3), temsilcinin kendisi için tür parametrelerini belirtir.

Temsilci türünün dönüş türü veya voidçıkış açısından güvenli (§19.2.3.2) olmalıdır.

Temsilci türünün tüm parametre türleri giriş açısından güvenli olmalıdır (§19.2.3.2). Ayrıca, tüm çıkış veya başvuru parametre türleri de çıkış açısından güvenli olmalıdır.

Not: Yaygın uygulama kısıtlamaları nedeniyle çıkış parametrelerinin giriş açısından güvenli olması gerekir. son not

Ayrıca, temsilcinin herhangi bir tür parametresindeki her sınıf türü kısıtlaması, arabirim türü kısıtlaması ve tür parametresi kısıtlaması giriş açısından güvenli olacaktır.

C# içindeki temsilci türleri ad eşdeğeridir, yapısal olarak eşdeğer değildir.

Örnek:

delegate int D1(int i, double d);
delegate int D2(int c, double d);

Temsilci türü D1 ve D2 iki farklı tür olduğundan, özdeş imzalarına rağmen birbirinin yerine kullanılamaz.

son örnek

Diğer genel tür bildirimleri gibi, tür bağımsız değişkenleri de oluşturulacak bir temsilci türü oluşturmak için verilmelidir. Oluşturulan temsilci türünün parametre türleri ve dönüş türü, temsilci bildirimindeki her tür parametresi için oluşturulan temsilci türünün karşılık gelen tür bağımsız değişkeni olan değiştirilerek oluşturulur.

Temsilci türünü bildirmenin tek yolu bir delegate_declaration yoluyla yapılır. Her temsilci türü, türünden System.Delegatetüretilen bir başvuru türüdür. Her temsilci türü için gereken üyeler §21.3'te ayrıntılı olarak yer alır. Temsilci türleri örtük olarak sealedolur, bu nedenle bir temsilci türünden herhangi bir tür türetme izni verilmiyor. Ayrıca, öğesinden System.Delegatetüretilen temsilci olmayan bir sınıf türü bildirmek de mümkün değildir. System.Delegate kendisi bir temsilci türü değildir; tüm temsilci türlerinin türetildiği bir sınıf türüdür.

21.3 Üye temsilcisi

Her temsilci türü , §15.3.4'te Delegateaçıklandığı gibi sınıftan üyeleri devralır. Ayrıca, her temsilci türü, parametre listesi temsilci bildirimindeki parameter_list eşleşenInvoke veya ref_return_type ile eşleşen ve ref_kind temsilci bildirimindekiyle eşleşen başv temsilcileriiçin genel olmayan bir yöntem sağlayacaktır. Yöntem, Invoke en az içeren temsilci türü kadar erişilebilir olmalıdır. Invoke Yöntemini temsilci türünde çağırma, temsilci çağırma söz dizimini (§21.6) kullanmaya benzer.

Uygulamalar, temsilci türünde ek üyeler tanımlayabilir.

Örnekleme dışında, bir sınıfa veya sınıf örneğine uygulanabilen tüm işlemler sırasıyla bir temsilci sınıfına veya örneğine de uygulanabilir. Özellikle, türün System.Delegate üyelerine normal üye erişim söz dizimi aracılığıyla erişmek mümkündür.

21.4 Temsilci uyumluluğu

Aşağıdakilerin tümü doğruysa bir yöntem veya temsilci türü M temsilci türüyle uyumludur

  • D ve M aynı sayıda parametreye sahiptir ve içindeki D her parametre, içindeki Milgili parametreyle aynı başvuruya göre parametre değiştiricisine sahiptir.
  • Her değer parametresi için, içindeki parametre türünden içindeki ilgili parametre türüne bir kimlik dönüştürmesi (D10.2.2) veya örtük başvuru dönüştürmesi (M) vardır.
  • Her başvuruya göre parametre için içindeki parametre türü D , içindeki Mparametre türüyle aynıdır.
  • Aşağıdakilerden biri doğrudur:
    • hem D hem de Mdeğer döndürmez.
    • D ve M değere göre döndürür (§15.6.1, §21.2) ve dönüş türünden dönüş türüne M bir kimlik veya örtük başvuru dönüştürmesi Dvardır.
    • D ve M her ikisi de başv döndürür, dönüş türü ile dönüş türü MDarasında bir kimlik dönüştürmesi vardır ve her ikisi de aynı ref_kind sahiptir.

Bu uyumluluk tanımı, dönüş türünde kovaryans ve parametre türlerinde değişken karşıtılık sağlar.

Örnek:

delegate int D1(int i, double d);
delegate int D2(int c, double d);
delegate object D3(string s);

class A
{
    public static int M1(int a, double b) {...}
}

class B
{
    public static int M1(int f, double g) {...}
    public static void M2(int k, double l) {...}
    public static int M3(int g) {...}
    public static void M4(int g) {...}
    public static object M5(string s) {...}
    public static int[] M6(object o) {...}
}

ve A.M1 yöntemleriB.M1, aynı dönüş türüne ve D1parametre listesine sahip olduğundan hem temsilci türleriyle D2 hem de ile uyumludur. , ve yöntemleriB.M2, farklı dönüş türlerine B.M3 veya parametre listelerine sahip olduğundan ve B.M4temsilcileriyle D1 uyumsuz. D2 ve yöntemleri B.M5 , B.M6 temsilci türüyle D3uyumludur.

son örnek

Örnek:

delegate bool Predicate<T>(T value);

class X
{
    static bool F(int i) {...}
    static bool G(string s) {...}
}

yöntemi X.F temsilci türüyle Predicate<int> ve yöntemi X.G de temsilci türüyle Predicate<string>uyumludur.

son örnek

Not: Temsilci uyumluluğunun sezgisel anlamı, temsilcinin her çağrısının tür güvenliğini ihlal etmeden, isteğe bağlı parametreleri ve parametre dizilerini açık parametreler olarak ele almadan yöntemin çağrılmasıyla değiştirilebiliyorsa yöntemin temsilci türüyle uyumlu olmasıdır. Örneğin, aşağıdaki kodda:

delegate void Action<T>(T arg);

class Test
{
    static void Print(object value) => Console.WriteLine(value);

    static void Main()
    {
        Action<string> log = Print;
        log("text");
    }
}

Bir Print temsilcinin çağrıları Action<string> da yöntemin Action<string> geçerli bir çağrısı Print olacağından yöntemi temsilci türüyle uyumludur.

Yukarıdaki Print metodunun imzası örneğin Print(object value, bool prependTimestamp = false) olarak değiştirildiyse, Print metodu artık bu alt fıkranın kurallarıyla Action<string> uyumlu olmayacaktır.

son not

21.5 Temsilci örneği oluşturma

Temsilci örneği, bir temsilci türüne dönüştürme, temsilci birleşimi veya temsilci kaldırma işlemi olan bir delegate_creation_expression (§12.8.17.5) tarafından oluşturulur. Yeni oluşturulan temsilci örneği aşağıdakilerden birine veya daha fazlasına başvurur:

  • delegate_creation_expression başvuruda bulunan statik yöntem veya
  • hedef nesne (olamaznull) ve delegate_creation_expression başvurulan örnek yöntemi veya
  • Başka bir temsilci (§12.8.17.5).

Örnek:

delegate void D(int x);

class C
{
    public static void M1(int i) {...}
    public void M2(int i) {...}
}

class Test
{
    static void Main()
    {
        D cd1 = new D(C.M1); // Static method
        C t = new C();
        D cd2 = new D(t.M2); // Instance method
        D cd3 = new D(cd2);  // Another delegate
    }
}

son örnek

Temsilci örneği tarafından kapsüllenen yöntem kümesi çağırma listesi olarak adlandırılır. Tek bir yöntemden bir temsilci örneği oluşturulduğunda, bu yöntemi kapsüller ve çağırma listesinde yalnızca bir giriş bulunur. Ancak, temsilci olmayannull iki örnek birleştirildiğinde, iki veya daha fazla girdi içeren yeni bir çağırma listesi oluşturmak için çağrı listeleri (sol işlenen ve sağ işlenen sırasıyla) birleştirilir.

Tek bir temsilciden yeni bir temsilci oluşturulduğunda, sonuç çağırma listesinde yalnızca bir girdi vardır ve bu da kaynak temsilcidir (§12.8.17.5).

Temsilciler ikili + (§12.12.5) ve += işleçler (§12.23.4) kullanılarak birleştirilir. Temsilci, ikili - (§12.12.6) ve -= işleçler (§12.23.4) kullanılarak temsilci birleşiminden kaldırılabilir. Temsilciler eşitlik için karşılaştırılabilir (§12.14.9).

Örnek: Aşağıdaki örnek, bir dizi temsilcinin ve buna karşılık gelen çağrı listelerinin örneğini gösterir:

delegate void D(int x);

class C
{
    public static void M1(int i) {...}
    public static void M2(int i) {...}
}

class Test
{
    static void Main() 
    {
        D cd1 = new D(C.M1); // M1 - one entry in invocation list
        D cd2 = new D(C.M2); // M2 - one entry
        D cd3 = cd1 + cd2;   // M1 + M2 - two entries
        D cd4 = cd3 + cd1;   // M1 + M2 + M1 - three entries
        D cd5 = cd4 + cd3;   // M1 + M2 + M1 + M1 + M2 - five entries
        D td3 = new D(cd3);  // [M1 + M2] - ONE entry in invocation
                             // list, which is itself a list of two methods.
        D td4 = td3 + cd1;   // [M1 + M2] + M1 - two entries
        D cd6 = cd4 - cd2;   // M1 + M1 - two entries in invocation list
        D td6 = td4 - cd2;   // [M1 + M2] + M1 - two entries in invocation list,
                             // but still three methods called, M2 not removed.
   }
}

ve cd1 örneği oluşturulurkencd2, her biri bir yöntemi kapsüller. Örneklendiğinde cd3 , bu sırada ve olmak üzere iki yöntemin M1M2çağrı listesi bulunur. cd4'nin çağırma listesinde M1, M2ve M1, sırasıyla bulunur. içincd5, çağrı listesi M1bu sırada , M2M1, , M1ve M2içerir.

delegate_creation_expression başka bir temsilciden temsilci oluştururken, sonuç özgünden farklı bir yapıya sahip bir çağrı listesine sahiptir, ancak bu da aynı yöntemlerin aynı sırayla çağrılmasına neden olur. Çağrı td3 listesinden cd3 oluşturulduğunda yalnızca bir üye vardır, ancak bu üye yöntemlerin M1 listesidir ve M2 bu yöntemler tarafından td3cd3tarafından çağrıldıkları sırayla çağrılır. Benzer şekilde, çağrı listesinin örneği oluşturulurken td4 yalnızca iki giriş vardır, ancak aynı şekilde üç yöntemi M1( M2ve M1) bu sırayla cd4 çağırır.

Çağırma listesinin yapısı temsilci çıkarma işlemini etkiler. ' cd6den çıkarılarak cd2 oluşturulan (çağıranM2) temsilci , ve çağrılarını cd4M1M2(, M1ve M1) çağırır.M1 td6Ancak , öğesinden cd2 çıkarılarak M2 oluşturulan (çağıran td4) ( , M1ve M2) öğesini çağıran M1temsilci, listedeki tek bir girdi değil, iç içe bir listenin üyesi olduğu gibi M1 , ve M2'yi bu sırayla çağırmaya devam ederM1M2. Temsilcileri birleştirme (kaldırmanın yanı sıra) hakkında daha fazla örnek için bkz. §21.6.

son örnek

Örnek oluşturulduktan sonra, temsilci örneği her zaman aynı çağrı listesine başvurur.

Not: İki temsilci birleştirildiğinde veya biri başka bir temsilciden kaldırıldığında, yeni bir temsilci kendi çağırma listesiyle sonuçlanır; birleştirilen veya kaldırılan temsilcilerin çağrı listeleri değişmeden kalır. son not

21.6 Temsilci çağırma

C# temsilci çağırmak için özel söz dizimi sağlar. Çağrı listesinde tek bir giriş bulunannull olmayan bir temsilci örneği çağrıldığında, kendisine verilen aynı bağımsız değişkenlerle tek bir yöntemi çağırır ve başvurulan yöntemle aynı değeri döndürür. (Temsilci çağırma hakkında ayrıntılı bilgi için bkz. §12.8.10.4.) Böyle bir temsilci çağrılırken bir özel durum oluşursa ve bu özel durum çağrılan yöntem içinde yakalanmazsa, temsilciyi çağıran yöntemde, bir özel durum yakalama yan tümcesi aranmasına devam edilir, sanki bu yöntem, temsilcinin başvurduğu yöntemi doğrudan çağırmış gibi.

Çağırma listesi birden çok girdi içeren bir temsilci örneğinin çağrılması, çağırma listesindeki yöntemlerin her birini sırayla zaman uyumlu olarak çağırarak devam eder. Çağrılan her yöntem, temsilci örneğine verilen bağımsız değişkenler kümesiyle aynı şekilde geçirilir. Böyle bir temsilci çağırması başvuru parametreleri (§15.6.2.3.3) içeriyorsa, her yöntem çağrısı aynı değişkene başvuruyla gerçekleşir; çağırma listesindeki bir yöntem tarafından bu değişkende yapılan değişiklikler, çağrı listesinin daha ilerisindeki yöntemlere görünür. Temsilci çağırması çıkış parametreleri veya dönüş değeri içeriyorsa, son değeri listedeki son temsilcinin çağrısından gelir. Böyle bir temsilcinin çağrılma işlemi sırasında bir özel durum oluşursa ve bu özel durum çağrılan yöntemde yakalanmazsa, özel durum yakalama yan tümcesi araması temsilciyi çağıran yöntemde devam eder ve çağrı listesinin daha aşağısındaki yöntemler çağrılmaz.

Değeri türünde nullbir özel durumla sonuçlanan bir temsilci örneği çağrılmaya çalışılıyorSystem.NullReferenceException.

Örnek: Aşağıdaki örnek, temsilcilerin örneğini oluşturma, birleştirme, kaldırma ve çağırma adımlarını gösterir:

delegate void D(int x);

class C
{
    public static void M1(int i) => Console.WriteLine("C.M1: " + i);

    public static void M2(int i) => Console.WriteLine("C.M2: " + i);

    public void M3(int i) => Console.WriteLine("C.M3: " + i);
}

class Test
{
    static void Main()
    {
        D cd1 = new D(C.M1);
        cd1(-1);             // call M1
        D cd2 = new D(C.M2);
        cd2(-2);             // call M2
        D cd3 = cd1 + cd2;
        cd3(10);             // call M1 then M2
        cd3 += cd1;
        cd3(20);             // call M1, M2, then M1
        C c = new C();
        D cd4 = new D(c.M3);
        cd3 += cd4;
        cd3(30);             // call M1, M2, M1, then M3
        cd3 -= cd1;          // remove last M1
        cd3(40);             // call M1, M2, then M3
        cd3 -= cd4;
        cd3(50);             // call M1 then M2
        cd3 -= cd2;
        cd3(60);             // call M1
        cd3 -= cd2;          // impossible removal is benign
        cd3(60);             // call M1
        cd3 -= cd1;          // invocation list is empty so cd3 is null
        // cd3(70);          // System.NullReferenceException thrown
        cd3 -= cd1;          // impossible removal is benign
    }
}

deyiminde cd3 += cd1;gösterildiği gibi, bir temsilci bir çağrı listesinde birden çok kez bulunabilir. Bu durumda, her oluşumda bir kez çağrılır. Bunun gibi bir çağrı listesinde, bu temsilci kaldırıldığında, çağrı listesindeki son oluşum aslında kaldırılandır.

Son deyiminin yürütülmesinden hemen önce, cd3 -= cd1temsilci cd3 boş bir çağırma listesine başvurur. Boş bir listeden temsilci kaldırmaya (veya boş olmayan bir listeden var olmayan bir temsilciyi kaldırmaya) çalışmak hata değildir.

Üretilen çıkış:

C.M1: -1
C.M2: -2
C.M1: 10
C.M2: 10
C.M1: 20
C.M2: 20
C.M1: 20
C.M1: 30
C.M2: 30
C.M1: 30
C.M3: 30
C.M1: 40
C.M2: 40
C.M3: 40
C.M1: 50
C.M2: 50
C.M1: 60
C.M1: 60

son örnek