Olay işleyicisi yürütme sırası


95

Birden çok olay işleyicisi kurarsam, şöyle ki:

_webservice.RetrieveDataCompleted += ProcessData1;
_webservice.RetrieveDataCompleted += ProcessData2;

olay RetrieveDataCompletedtetiklendiğinde işleyiciler hangi sırada çalışır ? Kayıt sırasına göre aynı iş parçacığında ve sırayla mı çalışıyorlar?


2
Cevap, RetrieveDataCompleted olayına özel olacaktır. Çoklu yayın temsilcisinin varsayılan yedekleme deposuna sahipse, evet "bunlar aynı iş parçacığında ve kayıtlı sırayla sırayla çalışır".
HappyNomad

Yanıtlar:


134

Şu anda, kaydedildikleri sırayla yürütülüyorlar. Ancak, bu bir uygulama ayrıntısıdır ve bu davranışın ilerideki sürümlerde aynı kalmasına güvenmem çünkü şartnameler tarafından gerekli değildir.


6
Merak ediyorum, neden olumsuz oylar? Bu tam olarak doğru ve soruyu doğrudan yanıtlıyor ...
Reed Copsey

2
@Rawling: Bu ikili operatör aşırı yük çözümü içindir - olay işleme için değil. Bu durumda, toplama operatörü bu değildir.
Reed Copsey

2
Ah, nerede yanlış yaptığımı görüyorum: "Olay işleyicileri delegedir, değil mi?". Artık olmadıklarını biliyorum. Kendime işleyicileri ters sırayla
işten atan

16
Açıklığa kavuşturmak gerekirse, sipariş, belirli bir etkinliğin destek mağazasına bağlıdır. Olaylar için varsayılan yedekleme deposu, çoklu yayın delegeleri, kayıt sırasına göre yürütülüyor olarak belgelenir. Bu, gelecekteki bir çerçeve sürümünde değişmeyecektir. Değişebilecek şey, belirli bir olay için kullanılan destek deposudur.
HappyNomad

6
Gerçekte 2 puan yanlış olduğu için olumsuz oy verildi. 1) Şu anda belirli olayın uygulanmasının dikte ettiği sırayla yürütülürler - çünkü olaylar için kendi ekleme / kaldırma yöntemlerinizi uygulayabilirsiniz. 2) Çoklu atamalı delegeler aracılığıyla varsayılan olay uygulamasını kullanırken, sıra aslında spesifikasyonlar için gereklidir.
Søren Boisen

54

Bir temsilcinin çağrı listesi, listenin her öğesinin temsilci tarafından çağrılan yöntemlerden tam olarak birini çağırdığı sıralı bir temsilciler kümesidir. Bir çağrı listesi yinelenen yöntemler içerebilir. Çağrı sırasında, temsilci, çağrı listesinde göründükleri sırayla yöntemleri çağırır .

Buradan: Sınıfı Delege Edin


1
Güzel, ancak addve removeanahtar sözcüklerini kullanarak bir olayın çoklu oyuncu temsilcisi olarak uygulanması gerekmeyebilir.
HappyNomad

Bob'unki gibi , diğer cevaplar bunun olay işleyicileriyle kullanılmasının güvenilmez olarak alınması gereken bir şey olduğundan bahsediyor ... bu doğru olsun ya da olmasın, bu cevap da bundan bahsedebilir.
n611x007

13

Tüm işleyicileri çıkararak ve ardından istediğiniz sırada yeniden ekleyerek sıralamayı değiştirebilirsiniz.

public event EventHandler event1;

public void ChangeHandlersOrdering()
{
    if (event1 != null)
    {
        List<EventHandler> invocationList = event1.GetInvocationList()
                                                  .OfType<EventHandler>()
                                                  .ToList();

        foreach (var handler in invocationList)
        {
            event1 -= handler;
        }

        //Change ordering now, for example in reverese order as follows
        for (int i = invocationList.Count - 1; i >= 0; i--)
        {
            event1 += invocationList[i];
        }
    }
}

10

Sıra keyfi. Bir çağrıdan diğerine belirli bir sırada çalıştırılan işleyicilere güvenemezsiniz.

Düzenleme: Ayrıca - bu sadece meraktan kaynaklanmadıkça - bilmeniz gereken gerçek, ciddi bir tasarım sorununun göstergesidir .


3
Sıra, belirli olayın uygulanmasına bağlıdır, ancak keyfi değildir . Olayın dokümantasyonu çağrı sırasını göstermediği sürece, buna bağlı olmanın riskli olduğunu kabul ediyorum. Bu bağlamda, bir takip sorusu gönderdim .
HappyNomad

9
Farklı sınıflar tarafından parçalı bir düzende ele alınması gereken bir olaya sahip olmak bana ciddi bir tasarım sorunu gibi görünmüyor. Olay kayıtları, sıranın önemli olduğunu bilmek için sırayı veya olayı bilmeyi zorlaştıracak şekilde yapılırsa sorun ortaya çıkacaktır.
Ignacio Soler Garcia

8

Kayıtlı oldukları sırayla çalıştırılırlar. RetrieveDataCompletedbir Çok Noktaya Yayın Delegesidir . Denemek ve doğrulamak için reflektörden bakıyorum ve her şeyi takip etmek için perde arkasında bir dizi kullanılmış gibi görünüyor.


diğer yanıtlar, olay işleyicilerde bunun "tesadüfi", "kırılgan", "uygulama ayrıntısı" vb. olduğunu not eder. herhangi bir standart veya konvansiyon gerektirmez, sadece olur. bu doğru mu? her durumda, bu cevap buna da atıfta bulunabilir.
n611x007

3

Birinin bunu System.Windows.Forms.Form bağlamında yapması gerekiyorsa, burada Shown olayının sırasını tersine çeviren bir örnek verilmiştir.

using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;

namespace ConsoleApplication {
    class Program {
        static void Main() {
            Form form;

            form = createForm();
            form.ShowDialog();

            form = createForm();
            invertShownOrder(form);
            form.ShowDialog();
        }

        static Form createForm() {
            var form = new Form();
            form.Shown += (sender, args) => { Console.WriteLine("form_Shown1"); };
            form.Shown += (sender, args) => { Console.WriteLine("form_Shown2"); };
            return form;
        }

        static void invertShownOrder(Form form) {
            var events = typeof(Form)
                .GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic)
                .GetValue(form, null) as EventHandlerList;

            var shownEventKey = typeof(Form)
                .GetField("EVENT_SHOWN", BindingFlags.NonPublic | BindingFlags.Static)
                .GetValue(form);

            var shownEventHandler = events[shownEventKey] as EventHandler;

            if (shownEventHandler != null) {
                var invocationList = shownEventHandler
                    .GetInvocationList()
                    .OfType<EventHandler>()
                    .ToList();

                foreach (var handler in invocationList) {
                    events.RemoveHandler(shownEventKey, handler);
                }

                for (int i = invocationList.Count - 1; i >= 0; i--) {
                    events.AddHandler(shownEventKey, invocationList[i]);
                }
            }
        }
    }
}

2

Bir MulticastDelegate, bir veya daha fazla öğeden oluşan ve çağrı listesi adı verilen bağlantılı bir temsilci listesine sahiptir. Çok noktaya yayın temsilcisi çağrıldığında, çağrı listesindeki delegeler göründükleri sırayla eşzamanlı olarak çağrılır. Listenin yürütülmesi sırasında bir hata meydana gelirse, bir istisna atılır.


2

Bir çağrı sırasında, yöntemler çağrı listesinde göründükleri sırayla çağrılır.

Ancak kimse çağrı listesinin delegeleri eklendikleri sırayla tuttuğunu söylemiyor. Bu nedenle çağrı sırası garanti edilmez.


1

Bu, yeni olay işleyici işlevini çoklu huni çağrısı listesinde istediğiniz yere yerleştirecek bir işlevdir.

    private void addDelegateAt(ref YourDelegate initial, YourDelegate newHandler, int position)
    {
        Delegate[] subscribers = initial.GetInvocationList();
        Delegate[] newSubscriptions = new Delegate[subscribers.Length + 1];

        for (int i = 0; i < newSubscriptions.Length; i++)
        {
            if (i < position)
                newSubscriptions[i] = subscribers[i];
            else if (i==position)
                newSubscriptions[i] = (YourDelegate)newHandler;
            else if (i > position)
                newSubscriptions[i] = subscribers[i-1];
        }

        initial = (YourDelegate)Delegate.Combine(newSubscriptions);
    }

Daha sonra, kodunuzun uygun olduğu her yerde bir '- =' ile işlevi her zaman kaldırabilirsiniz.

PS - 'Konum' parametresi için herhangi bir hata işleme yapmıyorum.


0

Benzer bir problemim vardı. Benim durumumda çok kolay düzeltildi. + = Operatörünü kullanmayan bir temsilci hiç görmemiştim. Sorunum, her zaman sonuna bir delege eklenmesiyle çözüldü, diğerlerinin tümü her zaman en başa eklenir. OP'nin örneği şöyle bir şey olabilir:

    _webservice.RetrieveDataCompleted = _webservice.RetrieveDataCompleted + ProcessData1;
    _webservice.RetrieveDataCompleted = ProcessData2 + _webservice.RetrieveDataCompleted;

İlk durumda ProcessData1 en son çağrılacaktır. 2. durumda ilk olarak ProcessData2 çağrılacaktır.

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.