Urutan eksekusi event handler

93

Jika saya menyiapkan beberapa penangan acara, seperti ini:

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

urutan apa yang dijalankan penangan saat acara RetrieveDataCompleteddipecat? Apakah mereka berjalan di utas yang sama dan berurutan dalam urutan yang terdaftar?

Phillip Ngan
sumber
2
Jawabannya akan spesifik untuk acara RetrieveDataCompleted. Jika memiliki penyimpanan dukungan default dari delegasi multi-cast, maka ya "mereka berjalan di thread yang sama dan secara berurutan dalam urutan yang terdaftar".
HappyNomad

Jawaban:

133

Saat ini, mereka dieksekusi sesuai urutan mereka terdaftar. Namun, ini adalah detail implementasi, dan saya tidak akan mengandalkan perilaku ini tetap sama di versi mendatang, karena tidak diwajibkan oleh spesifikasi.

Reed Copsey
sumber
6
Saya bertanya-tanya, mengapa suara negatifnya? Ini benar, dan menjawab pertanyaan secara langsung ...
Reed Copsey
2
@Rawling: Itu untuk resolusi kelebihan beban operator biner - bukan penanganan event. Ini bukan operator penjumlahan, dalam kasus ini.
Reed Copsey
2
Ah, saya mengerti kesalahan saya: "Penangan acara adalah delegasi, bukan?". Sekarang saya tahu mereka tidak. Telah menulis sendiri peristiwa yang memicu penangan dalam urutan terbalik, hanya untuk membuktikannya pada diri saya sendiri :)
Rawling
16
Untuk memperjelas, pesanan bergantung pada penyimpanan dukungan untuk acara tertentu. Penyimpanan cadangan default untuk acara, delegasi multi-cast, didokumentasikan sebagai dijalankan dalam urutan pendaftaran. Ini tidak akan berubah dalam versi kerangka kerja yang akan datang. Apa yang mungkin berubah adalah penyimpanan dukungan yang digunakan untuk acara tertentu.
HappyNomad
6
Tidak disukai karena secara faktual salah pada 2 poin. 1) Saat ini mereka dieksekusi dalam urutan yang ditentukan oleh implementasi acara tertentu - karena Anda dapat menerapkan metode tambah / hapus Anda sendiri untuk acara. 2) Saat menggunakan implementasi acara default melalui delegasi multi-cast, urutan sebenarnya diperlukan oleh spesifikasi.
Søren Boisen
53

Daftar pemanggilan delegasi adalah sekumpulan delegasi yang diurutkan di mana setiap elemen daftar memanggil tepat satu metode yang dipanggil oleh delegasi. Daftar permintaan dapat berisi metode duplikat. Selama pemanggilan, seorang delegasi memanggil metode dalam urutan kemunculannya dalam daftar pemanggilan .

Dari sini: Kelas Delegasi

Philip Wallace
sumber
1
Bagus, tetapi menggunakan kata kunci adddan removesebuah acara belum tentu diimplementasikan sebagai delegasi multi-pemain.
HappyNomad
Seperti halnya Bob , jawaban lain menyebutkan bahwa penggunaan ini dengan event handler adalah sesuatu yang harus dianggap tidak dapat diandalkan ... apakah itu benar atau tidak, jawaban ini juga bisa berbicara tentang itu.
n611x007
12

Anda dapat mengubah urutan dengan melepaskan semua penangan, lalu memasang kembali dalam urutan yang diinginkan.

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];
        }
    }
}
Naser Asadi
sumber
10

Urutannya sewenang-wenang. Anda tidak dapat mengandalkan penangan yang dieksekusi dalam urutan tertentu dari satu pemanggilan ke pemanggilan berikutnya.

Sunting: Dan juga - kecuali ini hanya karena ingin tahu - fakta yang perlu Anda ketahui merupakan indikasi masalah desain yang serius .

Rex M
sumber
3
Urutannya bergantung pada implementasi peristiwa tertentu, tetapi tidak sewenang - wenang. Kecuali jika dokumentasi acara menunjukkan urutan permintaan, saya setuju berisiko untuk bergantung padanya. Dalam nada itu, saya memposting pertanyaan lanjutan .
HappyNomad
9
Untuk memiliki acara yang perlu ditangani oleh kelas yang berbeda dalam urutan partircular tampaknya bukan masalah desain yang serius bagi saya. Masalah akan terjadi jika registrasi event dilakukan dengan cara yang membuat sulit untuk mengetahui order atau event untuk mengetahui bahwa order itu penting.
Ignacio Soler Garcia
8

Mereka dijalankan sesuai urutan pendaftarannya. RetrieveDataCompletedadalah Delegasi Multicast . Saya melihat melalui reflektor untuk mencoba dan memverifikasi, dan sepertinya array digunakan di belakang layar untuk melacak semuanya.

Bob
sumber
jawaban lain mencatat bahwa dengan event handler ini adalah 'tidak disengaja', 'rapuh', 'detail implementasi', dll., mis. tidak disyaratkan oleh standar atau konvensi apa pun, itu terjadi begitu saja. Apakah itu benar? bagaimanapun juga, jawaban ini bisa merujuk pada hal itu juga.
n611x007
3

Jika seseorang perlu melakukan ini dalam konteks System.Windows.Forms.Form, berikut adalah contoh membalik urutan acara Tampil.

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]);
                }
            }
        }
    }
}
Fábio Augusto Pandolfo
sumber
2

MulticastDelegate memiliki daftar delegasi yang ditautkan, yang disebut daftar permintaan, yang terdiri dari satu atau lebih elemen. Saat delegasi multicast dipanggil, delegasi dalam daftar pemanggilan dipanggil secara sinkron dalam urutan kemunculannya. Jika kesalahan terjadi selama eksekusi daftar maka pengecualian dilemparkan.

Rahul
sumber
2

Selama pemanggilan, metode dipanggil dalam urutan kemunculannya dalam daftar pemanggilan.

Tetapi tidak ada yang mengatakan bahwa daftar pemanggilan mempertahankan delegasi dalam urutan yang sama saat mereka ditambahkan. Dengan demikian, urutan permintaan tidak dijamin.

ruslanu
sumber
1

Ini adalah fungsi yang akan menempatkan fungsi penanganan kejadian baru di mana pun Anda inginkan dalam daftar pemanggilan multidelegate.

    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);
    }

Kemudian Anda selalu dapat menghapus fungsi dengan '- =' di mana pun dalam kode Anda.

PS - Saya tidak melakukan penanganan kesalahan untuk parameter 'posisi'.

chara
sumber
0

Saya punya masalah serupa. Dalam kasus saya, itu diperbaiki dengan sangat mudah. Saya belum pernah melihat delegasi yang tidak menggunakan operator + =. Masalah saya diperbaiki dengan memiliki satu delegasi yang selalu ditambahkan di akhir, yang lainnya selalu ditambahkan di awal. Contoh OP akan menjadi seperti ini:

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

Dalam kasus pertama, ProcessData1 akan dipanggil terakhir. Dalam kasus kedua, ProcessData2 akan dipanggil terlebih dahulu.

Tagihan
sumber