Etkinlikler ve temsilciler ve ilgili uygulamaları arasındaki fark [kapalı]


107

Delegelere göre olayları kullanmanın sözdizimsel şeker olmanın dışında avantajlarını görmüyorum. Belki yanlış anlıyorum, ama öyle görünüyor ki bu olay delege için sadece bir yer tutucusu.

Farklılıkları ve hangisini ne zaman kullanacağımı bana açıklar mısın? avantajları ve dezavantajları nelerdir? Kodumuz büyük ölçüde olaylara dayanıyor ve ben onun altına inmek istiyorum.

Temsilcileri etkinlikler yerine ne zaman kullanırsınız ve bunun tersi de geçerlidir? Lütfen üretim kodunda her ikisiyle de gerçek dünya deneyiminizi belirtin.


Evet, başımı farklılıkların etrafına sarmak gerçekten zordu, aynı görünüyorlar ve ilk bakışta aynısını yapıyorlar
Robert Gould

1
Ayrıca bu soruya bakın .
Dimitri C.

1
İki olay ve delegeler arasındaki fark bir fikir değil, gerçektir. Soru, teknolojilerin çözdüğü problemlerdeki farklılıkları gösterdikleri için ilgili uygulamaları sorar. Bu da bir görüş meselesi değil çünkü kimse hangisinin en iyisi olduğunu sormadı. Bu sorunun hiçbir kısmı bir görüş meselesi değildir ve bu ifade de bir fikir değildir. Bence. Rozetini aldın mı?
Peter Wone

Yanıtlar:


49

Teknik açıdan bakıldığında, diğer cevaplar farklılıkları ele aldı.

Anlambilim açısından olaylar, belirli koşullar karşılandığında bir nesne tarafından oluşturulan eylemlerdir. Örneğin, Hisse Senedi sınıfımın Limit adında bir özelliği var ve hisse senedi fiyatları Limite ulaştığında bir olay başlatıyor. Bu bildirim, bir olay aracılığıyla yapılır. Birinin bu olayı gerçekten önemsemesi ve buna abone olup olmaması, sahip sınıfının endişesi dışındadır.

Temsilci, C / C ++ terimleriyle bir işaretçiye benzer bir yapıyı tanımlamak için daha genel bir terimdir. .Net'teki tüm temsilciler çok noktaya yayın delegeleridir. Anlambilim açısından bakıldığında, genellikle bir tür girdi olarak kullanılırlar. Özellikle, Strateji Modelini uygulamanın mükemmel bir yoludur . Örneğin, bir nesne listesini sıralamak istersem, uygulamaya iki nesneyi nasıl karşılaştıracağını söylemek için yönteme bir Karşılaştırıcı stratejisi sağlayabilirim.

Üretim kodunda iki yöntemi kullandım. Veri nesnelerimin tonları, belirli özellikler karşılandığında bildirimde bulunur. En temel örnek, bir özellik değiştiğinde, bir PropertyChanged olayı oluşturulur (bkz. INotifyPropertyChanged arabirimi). Delegeleri kodda, belirli nesneleri dizeye dönüştürmek için farklı stratejiler sağlamak için kullandım. Bu özel örnek, belirli bir nesne türünün kullanıcılara gösterilmesi için uygulamaların yüceltilmiş bir ToString () listesiydi.


4
Belki bir şeyi kaçırıyorum, ancak bir Olay İşleyici bir tür temsilci değil mi?
Powerlord

1
Cevabım Düzenleme # 1 ve # 2 sorularına hitap ediyor; kullanım açısından farklılıklar. Bu tartışmanın amaçları açısından, teknik açıdan farklı olsa da, haklısınız. Teknik farklılıklar için diğer cevaplara bir göz atın.
Szymon Rozga

3
".Net'teki tüm temsilciler çok noktaya yayın delegeleridir"? Değerleri döndüren delegeler bile mi?
Qwertie

5
Evet. Geçmiş için msdn.microsoft.com/en-us/magazine/cc301816.aspx sayfasına bakın . Şuraya bakın : msdn.microsoft.com/en-us/library/system.delegate.aspx . Değer döndürürlerse, döndürülen değer zincirdeki son temsilcinin değerlendirmesidir.
Szymon Rozga

temsilciler, abone sınıfında tanımlanan olay işleyicilere işaret eden başvuru türleridir. Diğer bir deyişle, delege olay (yayıncıda) ve abonede tanımlanan olay işleyici arasında bir bağlantı olarak kullanılır. Bir uygulamada, birden fazla abonenin bir olayı dinlemesi gerekecektir ve bu tür senaryolarda delegeler bize yayıncı ve aboneleri birbirine bağlamak için verimli bir yol sunar.
josepainumkal

55

Anahtar sözcük event, çok noktaya yayın temsilcileri için bir kapsam değiştiricidir. Bununla çok noktaya yayın delege bildirmek arasındaki pratik farklar şunlardır:

  • eventBir arayüzde kullanabilirsiniz .
  • Çok noktaya yayın temsilcisine çağrı erişimi, bildiren sınıfla sınırlıdır. Davranış, temsilcinin çağrı için özel olması gibidir. Atama amacıyla erişim, açık bir erişim değiştiricisi tarafından belirtildiği gibidir (örn. public event).

İlgi alanı olarak, çok noktaya yayın delegelerine başvurabilirsiniz +ve -bu, delegelerin olaylara kombinasyon ataması için +=ve -=sözdiziminin temelini oluşturur . Bu üç kod parçacığı eşdeğerdir:

B = new EventHandler(this.MethodB);
C = new EventHandler(this.MethodC);
A = B + C;

Hem doğrudan atamayı hem de kombinasyon atamayı gösteren örnek iki.

B = new EventHandler(this.MethodB);
C = new EventHandler(this.MethodC);
A = B;
A += C;

Üçüncü örnek: daha tanıdık sözdizimi. Muhtemelen tüm işleyicileri kaldırmak için null atamasını biliyorsunuzdur.

B = new EventHandler(this.MethodB);
C = new EventHandler(this.MethodC);
A = null;
A += B;
A += C;

Özellikler gibi, olayların da hiç kimsenin kullanmadığı tam bir sözdizimi vardır. Bu:

class myExample 
{
  internal EventHandler eh;

  public event EventHandler OnSubmit 
  { 
    add 
    {
      eh = Delegate.Combine(eh, value) as EventHandler;
    }
    remove
    {
      eh = Delegate.Remove(eh, value) as EventHandler;
    }
  }

  ...
}

... bununla tam olarak aynı şeyi yapar :

class myExample 
{
  public event EventHandler OnSubmit;
}

Ekle ve kaldır yöntemleri, VB.NET'in kullandığı oldukça stilize sözdiziminde daha belirgindir (operatör aşırı yüklemesi yoktur).


6
+ "Çok noktaya yayın temsilcisine çağrı erişimi bildiren sınıfla sınırlıdır" - bu benim için temsilciler ve olaylar arasındaki temel fark noktasıdır.
RichardOD

2
Bir diğer önemli fark (aşağıda itowlson tarafından belirtilmiştir), bir olaya atama yaparak tüm olay işleyicilerinin aboneliğini iptal edememesidir, ancak bunu bir temsilci ile yapabilirler. (Bu arada, tüm bunlardan bana en faydalı cevap seninki idi).
Roman Starkov

4
Google ve stackoverflow ne kadar kullanışlı olsa da, tüm bunlar ve daha fazlası, Microsoft'tan ücretsiz olarak halka açık olan C # dil spesifikasyonunda akıllara durgunluk veren ayrıntılarda mevcuttur. Görünüşe bakılırsa, kılavuzu Tanrı'nın yarattığını ve Jon Skeet'in onu yuttuğunu biliyorum, ancak başka kopyalar da var :)
Peter Wone

12

Olaylar sözdizimsel şekerdir. Lezzetliler. Bir olay gördüğümde ne yapacağımı biliyorum. Bir temsilci gördüğümde, o kadar emin değilim.

Olayları arayüzlerle (daha fazla şeker) birleştirmek, ağız sulandıran bir atıştırmalık yapar. Delegeler ve saf sanal soyut sınıflar çok daha az iştah açıcıdır.


ben de öyle görüyorum. Daha derin ve daha tatlı bir açıklama istiyorum :)

13
Çok fazla şeker bir yağ yapar, ancak ... = P
Erik Forbes

5

Olaylar, meta verilerde olduğu gibi işaretlenir. Bu, Windows Forms veya ASP.NET tasarımcıları gibi şeylerin olayları yalnızca temsilci türünün özelliklerinden ayırt etmesine ve bunlar için uygun destek sağlamasına (özellikle bunları Özellikler penceresinin Olaylar sekmesinde göstermesine) olanak tanır.

Temsilci türünün bir özelliğinden başka bir fark, kullanıcıların yalnızca olay işleyicileri ekleyip kaldırabilmeleridir, oysa temsilci türünün bir özelliği ile değeri ayarlayabilirler:

someObj.SomeCallback = MyCallback;  // okay, replaces any existing callback
someObj.SomeEvent = MyHandler;  // not okay, must use += instead

Bu, olay abonelerini izole etmeye yardımcı olur: İşleyicimi bir olaya ekleyebilirim ve işleyicinizi aynı olaya ekleyebilir ve yanlışlıkla işleyicimin üzerine yazmazsınız.


4

Olaylar tipik olarak çok noktaya yayın delegeleri ile uygulanmasına rağmen, bu şekilde kullanılmalarına gerek yoktur. Bir sınıf olayı ortaya çıkarırsa, bu, sınıfın iki yöntemi ortaya çıkardığı anlamına gelir. Özünde anlamları şöyledir:

  1. İşte bir temsilci. Lütfen ilginç bir şey olduğunda onu çağırın.
  2. İşte bir temsilci. Uygun olan tüm referansları yok etmelisiniz (ve artık onu aramamalısınız).

Bir sınıfın ortaya çıkardığı bir olayı işlemesinin en yaygın yolu, bir çok noktaya yayın temsilcisi tanımlamak ve yukarıdaki yöntemlere geçirilen herhangi bir temsilci eklemek / kaldırmaktır, ancak bu şekilde çalışmasına gerek yoktur. Ne yazık ki, olay mimarisi, alternatif yaklaşımları çok daha temiz hale getirecek bazı şeyleri yapamıyor (örneğin, abonelik yönteminin abone tarafından saklanacak bir MethodInvoker döndürmesi; bir olayın aboneliğini iptal etmek için, yalnızca döndürülen yöntemi çağırın) çok noktaya yayın delegeleri açık ara en yaygın yaklaşımdır.


4

farklılıkları anlamak için bu 2 örneğe bakabilirsiniz

Temsilcilerle Örnek (Bu durumda, değer döndürmeyen bir tür temsilci olan eylem)

public class Animal
{
    public Action Run {get; set;}

    public void RaiseEvent()
    {
        if (Run != null)
        {
            Run();
        }
    }
}

Temsilciyi kullanmak için böyle bir şey yapmalısınız

Animale animal= new Animal();
animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running") ;
animal.RaiseEvent();

bu kod iyi çalışıyor ancak bazı zayıf noktalarınız olabilir.

Örneğin bunu yazarsam

animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running");
animal.Run = () => Console.WriteLine("I'm sleeping") ;

son kod satırıyla önceki davranışları geçersiz kıldığımdan yalnızca biri eksik +( +yerine kullandım +=)

Başka bir zayıf nokta sizin kullandığınız her sınıf olmasıdır Animalsınıfını yükseltebilirsiniz RaiseEventçağırarak sadece animal.RaiseEvent().

Bu zayıf noktalardan kaçınmak için eventsc # ' da kullanabilirsiniz .

Hayvan sınıfınız bu şekilde değişecek

public class ArgsSpecial :EventArgs
   {
        public ArgsSpecial (string val)
        {
            Operation=val;
        }

        public string Operation {get; set;}
   } 



 public class Animal
    {
       public event EventHandler<ArgsSpecial> Run = delegate{} //empty delegate. In this way you are sure that value is always != null because no one outside of the class can change it

       public void RaiseEvent()
       {  
          Run(this, new ArgsSpecial("Run faster"));
       }
    }

olayları aramak için

 Animale animal= new Animal();
 animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation);
 animal.RaiseEvent();

Farklılıklar:

  1. Bir genel mülk değil, genel bir alan kullanıyorsunuz (olaylarla, derleyici alanlarınızı istenmeyen erişime karşı korur)
  2. Olaylar doğrudan atanamaz. Bu durumda, davranışı geçersiz kılarak gösterdiğim önceki hatayı yapamazsınız.
  3. Sınıfınızın dışından hiç kimse etkinliği düzenleyemez.
  4. Olaylar bir arayüz bildirimine dahil edilebilirken bir alan olamaz

notlar

EventHandler aşağıdaki temsilci olarak bildirildi:

public delegate void EventHandler (object sender, EventArgs e)

bir göndereni (Nesne tipinde) ve olay argümanlarını alır. Statik yöntemlerden geliyorsa gönderen boştur.

Bunun EventHAndleryerine şu örneği de kullanabilirsiniz:EventHandler<ArgsSpecial>

EventHandler ile ilgili belgeler için buraya bakın


3

Düzenleme # 1 Delegeleri etkinlikler ve tersler yerine ne zaman kullanırsınız? Lütfen üretim kodunda her ikisiyle de gerçek dünya deneyiminizi belirtin.

Kendi API'lerimi tasarladığımda, yöntemlere veya sınıfların yapıcılarına parametre olarak iletilen temsilciler tanımlıyorum:

  • Böylece bir yöntem basit bir 'şablon yöntemi' modeli uygulayabilir (örneğin Predicateve Actiontemsilciler .Net genel koleksiyon sınıflarına geçirilir)
  • Veya sınıfın bir 'geri arama' (genellikle onu oluşturan sınıfın bir yöntemine geri arama) yapabilmesi için.

Bu temsilciler genellikle çalışma zamanında isteğe bağlı değildir (yani olmamalıdır null).

Olayları kullanmama eğilimindeyim; Ben kullanım olayları nerede yapmak ama ben için bunları kullanmak isteğe sinyal olaylarını sıfıra birini veya daha fazlasını müşterilerine olabilir ilgilenmiycem, yani bir sınıf (örneğin mantıklı olduğunda System.Windows.Formsınıfı) olması gereken ve herhangi bir istemci olup olmadığını çalıştırmak olayına bir olay işleyicisi ekledi (örneğin, formun 'fare aşağı' olayı mevcuttur, ancak herhangi bir harici istemcinin o olaya bir olay işleyicisi kurmakla ilgilenip ilgilenmediği isteğe bağlıdır ).


2

Bunun için teknik bir nedenim olmasa da, olayları UI tarzı kodda, başka bir deyişle kodun daha yüksek seviyelerinde kullanıyorum ve kodda daha derin olan mantık için delegeler kullanıyorum. Her ikisini de kullanabilirsiniz dediğim gibi, ancak bu kullanım modelini mantıksal olarak sağlam buluyorum, başka hiçbir şey değilse, geri arama türlerini ve bunların hiyerarşisini de belgelemeye yardımcı oluyor.


Düzenleme: Sanırım kullanım biçimlerindeki fark şu olurdu, olayları görmezden gelmeyi tamamen kabul edilebilir buluyorum, bunlar kancalar / taslaklar, olayı bilmeniz gerekiyorsa, umursamıyorsanız onları dinleyin olay onu görmezden geliyor. Bu yüzden onları UI için kullanıyorum, bir tür Javascript / Tarayıcı olay stili. Bununla birlikte, bir temsilcim olduğunda, GERÇEKTEN birisinin temsilcinin görevini yerine getirmesini ve ele alınmazsa bir istisna atmasını bekliyorum.


Ben de UI'de evens'i kullandığım için bunu biraz daha detaylandırır mısınız? İyi bir örnek yeterli olur .... teşekkürler


1

Olay yerine yalnızca temsilci kullanırsak, abone aşağıdaki resimde gösterildiği gibi temsilcinin kendisini klonlama (), çağırma () fırsatına sahip olur. Hangisi doğru değil.

görüntü açıklamasını buraya girin

Bu, s / b olayı ve temsilci arasındaki temel farktır. abonenin yalnızca bir hakkı vardır, yani olayları dinleme

ConsoleLog sınıfı, EventLogHandler aracılığıyla günlük olaylarına abone oluyor

public class ConsoleLog
{
    public ConsoleLog(Operation operation)
    {
        operation.EventLogHandler += print;
    }

    public void print(string str)
    {
        Console.WriteLine("write on console : " + str);
    }
}

FileLog sınıfı, EventLogHandler aracılığıyla günlük olaylarına abone oluyor

public class FileLog
{
    public FileLog(Operation operation)
    {
        operation.EventLogHandler += print;
    }

    public void print(string str)
    {
        Console.WriteLine("write in File : " + str);
    }
}

İşlem sınıfı, günlük olaylarını yayınlıyor

public delegate void logDelegate(string str);
public class Operation
{
    public event logDelegate EventLogHandler;
    public Operation()
    {
        new FileLog(this);
        new ConsoleLog(this);
    }

    public void DoWork()
    {
        EventLogHandler.Invoke("somthing is working");
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.