Tanda Tangan Acara di .NET - Menggunakan 'Pengirim' yang Diketik Kuat?


106

Saya sepenuhnya menyadari bahwa apa yang saya usulkan tidak mengikuti pedoman NET, dan, oleh karena itu, mungkin ide yang buruk karena alasan ini saja. Namun, saya ingin mempertimbangkan ini dari dua kemungkinan perspektif:

(1) Haruskah saya mempertimbangkan untuk menggunakan ini untuk pekerjaan pengembangan saya sendiri, yang 100% untuk tujuan internal.

(2) Apakah ini konsep yang dapat dipertimbangkan oleh perancang kerangka kerja untuk diubah atau diperbarui?

Saya berpikir tentang menggunakan tanda tangan acara yang menggunakan 'pengirim' yang diketik kuat, alih-alih mengetiknya sebagai 'objek', yang merupakan pola desain .NET saat ini. Artinya, alih-alih menggunakan tanda tangan acara standar yang terlihat seperti ini:

class Publisher
{
    public event EventHandler<PublisherEventArgs> SomeEvent;
}

Saya sedang mempertimbangkan untuk menggunakan tanda tangan acara yang menggunakan parameter 'pengirim' yang diketik kuat, sebagai berikut:

Pertama, tentukan "StrongTypedEventHandler":

[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
    TSender sender,
    TEventArgs e
)
where TEventArgs : EventArgs;

Ini tidak jauh berbeda dari Action <TSender, TEventArgs>, tetapi dengan menggunakan StrongTypedEventHandler, kami memaksakan asal TEventArgs System.EventArgs.

Selanjutnya, sebagai contoh, kita dapat menggunakan StrongTypedEventHandler di kelas penerbitan sebagai berikut:

class Publisher
{
    public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;

    protected void OnSomeEvent()
    {
        if (SomeEvent != null)
        {
            SomeEvent(this, new PublisherEventArgs(...));
        }
    }
}

Pengaturan di atas akan memungkinkan pelanggan untuk menggunakan pengendali kejadian yang diketik kuat yang tidak memerlukan transmisi:

class Subscriber
{
    void SomeEventHandler(Publisher sender, PublisherEventArgs e)
    {           
        if (sender.Name == "John Smith")
        {
            // ...
        }
    }
}

Saya sepenuhnya menyadari bahwa ini melanggar pola penanganan peristiwa standar .NET; Namun, perlu diingat bahwa kontradiksi akan memungkinkan pelanggan untuk menggunakan tanda tangan penanganan acara tradisional jika diinginkan:

class Subscriber
{
    void SomeEventHandler(object sender, PublisherEventArgs e)
    {           
        if (((Publisher)sender).Name == "John Smith")
        {
            // ...
        }
    }
}

Artinya, jika penangan peristiwa perlu berlangganan peristiwa dari jenis objek yang berbeda (atau mungkin tidak diketahui), penangan dapat mengetikkan parameter 'pengirim' sebagai 'objek' untuk menangani keseluruhan objek pengirim potensial.

Selain melanggar konvensi (yang merupakan sesuatu yang tidak saya anggap enteng, percayalah) saya tidak dapat memikirkan kerugian apapun dari hal ini.

Mungkin ada beberapa masalah kepatuhan CLS di sini. Ini berjalan di Visual Basic .NET 2008 100% baik-baik saja (saya telah diuji), tetapi saya percaya bahwa versi Visual Basic .NET hingga 2005 tidak memiliki kovarian dan kontradiksi yang mendelegasikan. [Sunting: Saya telah menguji ini, dan sudah dikonfirmasi: VB.NET 2005 dan di bawahnya tidak dapat menangani ini, tetapi VB.NET 2008 100% baik-baik saja. Lihat "Edit # 2", di bawah.] Mungkin ada bahasa .NET lain yang juga bermasalah dengan ini, saya tidak yakin.

Tetapi saya tidak melihat diri saya mengembangkan bahasa apa pun selain C # atau Visual Basic .NET, dan saya tidak keberatan membatasinya ke C # dan VB.NET untuk .NET Framework 3.0 dan yang lebih baru. (Saya tidak bisa membayangkan kembali ke 2.0 pada saat ini, jujur.)

Adakah yang bisa memikirkan masalah dengan ini? Atau apakah ini hanya melanggar konvensi begitu banyak sehingga membuat perut orang mual?

Berikut beberapa tautan terkait yang saya temukan:

(1) Panduan Desain Acara [MSDN 3.5]

(2) C # Event Raising sederhana - menggunakan "sender" vs. EventArgs kustom [StackOverflow 2009]

(3) Pola tanda tangan acara di .net [StackOverflow 2008]

Saya tertarik dengan pendapat siapa pun dan semua orang tentang ini ...

Terima kasih sebelumnya,

Mike

Sunting # 1: Ini menanggapi posting Tommy Carlier :

Berikut adalah contoh kerja lengkap yang menunjukkan bahwa penangan kejadian yang diketik kuat dan penangan kejadian standar saat ini yang menggunakan parameter 'pengirim objek' dapat hidup berdampingan dengan pendekatan ini. Anda dapat menyalin-tempel kode dan menjalankannya:

namespace csScrap.GenericEventHandling
{
    class PublisherEventArgs : EventArgs
    {
        // ...
    }

    [SerializableAttribute]
    public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
        TSender sender,
        TEventArgs e
    )
    where TEventArgs : EventArgs;

    class Publisher
    {
        public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;

        public void OnSomeEvent()
        {
            if (SomeEvent != null)
            {
                SomeEvent(this, new PublisherEventArgs());
            }
        }
    }

    class StrongTypedSubscriber
    {
        public void SomeEventHandler(Publisher sender, PublisherEventArgs e)
        {
            MessageBox.Show("StrongTypedSubscriber.SomeEventHandler called.");
        }
    }

    class TraditionalSubscriber
    {
        public void SomeEventHandler(object sender, PublisherEventArgs e)
        {
            MessageBox.Show("TraditionalSubscriber.SomeEventHandler called.");
        }
    }

    class Tester
    {
        public static void Main()
        {
            Publisher publisher = new Publisher();

            StrongTypedSubscriber strongTypedSubscriber = new StrongTypedSubscriber();
            TraditionalSubscriber traditionalSubscriber = new TraditionalSubscriber();

            publisher.SomeEvent += strongTypedSubscriber.SomeEventHandler;
            publisher.SomeEvent += traditionalSubscriber.SomeEventHandler;

            publisher.OnSomeEvent();
        }
    }
}

Sunting # 2: Ini adalah tanggapan atas pernyataan Andrew Hare tentang kovariansi dan kontradiksi dan bagaimana penerapannya di sini. Delegasi dalam bahasa C # telah lama mengalami kovarian dan kontradiksi sehingga hanya terasa "intrinsik", tetapi sebenarnya tidak. Bahkan mungkin sesuatu yang diaktifkan di CLR, saya tidak tahu, tetapi Visual Basic .NET tidak mendapatkan kemampuan kovarian dan kontradiksi untuk delegasinya hingga .NET Framework 3.0 (VB.NET 2008). Dan sebagai hasilnya, Visual Basic.NET untuk .NET 2.0 dan di bawahnya tidak dapat menggunakan pendekatan ini.

Misalnya contoh di atas dapat diterjemahkan ke dalam VB.NET sebagai berikut:

Namespace GenericEventHandling
    Class PublisherEventArgs
        Inherits EventArgs
        ' ...
        ' ...
    End Class

    <SerializableAttribute()> _
    Public Delegate Sub StrongTypedEventHandler(Of TSender, TEventArgs As EventArgs) _
        (ByVal sender As TSender, ByVal e As TEventArgs)

    Class Publisher
        Public Event SomeEvent As StrongTypedEventHandler(Of Publisher, PublisherEventArgs)

        Public Sub OnSomeEvent()
            RaiseEvent SomeEvent(Me, New PublisherEventArgs)
        End Sub
    End Class

    Class StrongTypedSubscriber
        Public Sub SomeEventHandler(ByVal sender As Publisher, ByVal e As PublisherEventArgs)
            MessageBox.Show("StrongTypedSubscriber.SomeEventHandler called.")
        End Sub
    End Class

    Class TraditionalSubscriber
        Public Sub SomeEventHandler(ByVal sender As Object, ByVal e As PublisherEventArgs)
            MessageBox.Show("TraditionalSubscriber.SomeEventHandler called.")
        End Sub
    End Class

    Class Tester
        Public Shared Sub Main()
            Dim publisher As Publisher = New Publisher

            Dim strongTypedSubscriber As StrongTypedSubscriber = New StrongTypedSubscriber
            Dim traditionalSubscriber As TraditionalSubscriber = New TraditionalSubscriber

            AddHandler publisher.SomeEvent, AddressOf strongTypedSubscriber.SomeEventHandler
            AddHandler publisher.SomeEvent, AddressOf traditionalSubscriber.SomeEventHandler

            publisher.OnSomeEvent()
        End Sub
    End Class
End Namespace

VB.NET 2008 dapat menjalankannya 100% dengan baik. Tapi sekarang saya sudah mengujinya di VB.NET 2005, hanya untuk memastikan, dan itu tidak bisa dikompilasi, menyatakan:

Metode 'Public Sub SomeEventHandler (sender As Object, e As vbGenericEventHandling.GenericEventHandling.PublisherEventArgs)' tidak memiliki tanda tangan yang sama dengan delegasi 'Delegate Sub StrongTypedEventHandler (Of TSender, TEventArgs As System.EventArgs) (sender As PublisherEvent, e Asrgs) '

Pada dasarnya, delegasi tidak berubah di VB.NET versi 2005 dan di bawahnya. Saya benar-benar memikirkan ide ini beberapa tahun yang lalu, tetapi ketidakmampuan VB.NET untuk menangani ini mengganggu saya ... Tapi sekarang saya telah pindah dengan kokoh ke C #, dan VB.NET sekarang dapat mengatasinya, jadi, yah, karenanya posting ini.

Edit: Perbarui # 3

Oke, saya telah menggunakan ini dengan cukup berhasil untuk beberapa waktu sekarang. Ini benar-benar sistem yang bagus. Saya memutuskan untuk menamai "StrongTypedEventHandler" saya sebagai "GenericEventHandler", yang didefinisikan sebagai berikut:

[SerializableAttribute]
public delegate void GenericEventHandler<TSender, TEventArgs>(
    TSender sender,
    TEventArgs e
)
where TEventArgs : EventArgs;

Selain penggantian nama ini, saya menerapkannya persis seperti yang dibahas di atas.

Itu tersandung aturan FxCop CA1009, yang menyatakan:

"Menurut konvensi, peristiwa .NET memiliki dua parameter yang menentukan pengirim peristiwa dan data peristiwa. Tanda tangan pengendali peristiwa harus mengikuti formulir ini: void MyEventHandler (pengirim objek, EventArgs e). Parameter 'pengirim' selalu berjenis System.Object, bahkan jika dimungkinkan untuk menggunakan jenis yang lebih spesifik. Parameter 'e' selalu berjenis System.EventArgs. Peristiwa yang tidak menyediakan data peristiwa harus menggunakan jenis delegasi System.EventHandler. Penangan peristiwa mengembalikan void sehingga mereka dapat mengirim setiap peristiwa ke beberapa metode target. Nilai apa pun yang dikembalikan oleh target akan hilang setelah panggilan pertama. "

Tentu saja, kami mengetahui semua ini, dan bagaimanapun juga kami melanggar aturan. (Semua penangan acara dapat menggunakan 'Pengirim objek' standar dalam tanda tangannya jika diinginkan dalam hal apa pun - ini adalah perubahan yang tidak dapat dipecahkan.)

Jadi penggunaan a berhasil SuppressMessageAttribute:

[SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly",
    Justification = "Using strong-typed GenericEventHandler<TSender, TEventArgs> event handler pattern.")]

Saya berharap pendekatan ini menjadi standar di beberapa titik di masa mendatang. Ini benar-benar bekerja dengan sangat baik.

Terima kasih untuk semua pendapat kalian, saya sangat menghargainya ...

Mike


6
Lakukan. (Jangan berpikir ini membenarkan sebuah jawaban.)
Konrad Rudolph

1
Argumen saya tidak ditujukan pada Anda: tentu saja Anda harus melakukan ini dalam proyek Anda sendiri. Mereka adalah argumen mengapa itu mungkin tidak berhasil di BCL.
Tommy Carlier

3
Sobat, saya berharap proyek saya melakukan ini dari awal, saya benci mengirimkan pengirim.
Matt H

7
Sekarang INI adalah pertanyaan. Lihat, teman-teman? Bukan salah satu dari oh hi this my hom work solve it plz :code dump:pertanyaan berukuran tweet ini , tetapi pertanyaan yang kami pelajari .
Camilo Martin

3
Saran lain, hanya nama itu EventHandler<,>dari GenericEventHandler<,>. Sudah ada generik EventHandler<>di BCL yang dinamai hanya EventHandler. Jadi EventHandler adalah nama yang lebih umum dan delegasi mendukung kelebihan tipe
nawfal

Jawaban:


25

Tampaknya Microsoft telah mengambil ini karena contoh serupa sekarang ada di MSDN:

Delegasi Umum


2
+1 Ah, luar biasa. Mereka benar-benar memahami ini. Ini bagus. Namun, saya berharap mereka menjadikan ini pola yang dikenali dalam VS IDE, karena, seperti sekarang, lebih canggung menggunakan pola ini dalam kaitannya dengan IntelliSense, dll.
Mike Rosenblum

13

Apa yang Anda usulkan sebenarnya sangat masuk akal, dan saya hanya ingin tahu apakah ini salah satu dari hal-hal yang memang seperti itu karena awalnya dirancang sebelum obat generik, atau jika ada alasan nyata untuk ini.


1
Saya yakin inilah alasannya. Namun, sekarang versi bahasa yang lebih baru memiliki kontradiksi untuk menangani ini, sepertinya mereka harus dapat menangani ini dengan cara yang kompatibel ke belakang. Penangan sebelumnya yang menggunakan 'objek pengirim' tidak akan rusak. Tetapi ini tidak berlaku untuk bahasa lama dan mungkin tidak berlaku untuk beberapa bahasa .NET saat ini, saya tidak yakin.
Mike Rosenblum

13

Windows Runtime (WinRT) memperkenalkan TypedEventHandler<TSender, TResult>delegasi, yang melakukan apa yang Anda StrongTypedEventHandler<TSender, TResult>lakukan, tetapi tampaknya tanpa batasan pada TResultparameter tipe:

public delegate void TypedEventHandler<TSender, TResult>(TSender sender,
                                                         TResult args);

Dokumentasi MSDN ada di sini .


1
Ah, senang melihat ada kemajuan ... Aku bertanya-tanya mengapa TResult tidak dibatasi untuk mewarisi dari kelas 'EventArgs'. Kelas dasar 'EventArgs' pada dasarnya kosong; mungkinkah mereka menjauh dari batasan ini?
Mike Rosenblum

Ini mungkin pengawasan dari tim desain; siapa tahu.
Pierre Arnaud

baik, acara berfungsi dengan baik tanpa menggunakan EventArgs, itu hanya hal konvensi
Sebastian

3
Ini secara khusus menyatakan dalam dokumentasi TypedEventHandler yang argsakan menjadi nulljika tidak ada data kejadian, jadi tampaknya mereka menjauh dari menggunakan objek yang pada dasarnya kosong secara default. Saya menduga bahwa ide aslinya adalah bahwa metode dengan parameter tipe kedua EventArgsdapat menangani peristiwa apa pun karena jenisnya akan selalu kompatibel. Mereka mungkin sekarang menyadari bahwa kemampuan untuk menangani banyak kejadian berbeda dengan satu metode tidaklah terlalu penting.
jmcilhinney

1
Ini tidak terlihat seperti pengawasan. Batasan telah dihapus dari delegasi System.EventHandler <TEventArgs> juga. referenceource.microsoft.com/#mscorlib/system/…
colton7909

5

Saya mempermasalahkan pernyataan berikut:

  • Saya percaya bahwa versi Visual Basic .NET hingga 2005 tidak memiliki kovarian dan kontradiksi yang mendelegasikan.
  • Saya benar-benar menyadari bahwa ini hampir menodai agama.

Pertama-tama, tidak ada yang Anda lakukan di sini yang ada hubungannya dengan kovarian atau kontradiksi. ( Sunting: Pernyataan sebelumnya salah, untuk informasi lebih lanjut silakan lihat Kovarian dan Kontradiksi dalam Delegasi ) Solusi ini akan bekerja dengan baik di semua CLR versi 2.0 dan yang lebih baru (jelas ini tidak akan berfungsi di aplikasi CLR 1.0 karena menggunakan generik).

Kedua, saya sangat tidak setuju bahwa ide Anda mendekati "penghujatan" karena ini adalah ide yang bagus.


2
Hai Andrew, terima kasih atas jempolnya! Mempertimbangkan tingkat reputasi Anda, ini sangat berarti bagi saya ... Mengenai masalah kovarian / kontravarian: jika delegasi yang diberikan oleh pelanggan tidak sama persis dengan tanda tangan acara penerbit, maka kovarians dan pelanggaran akan dilibatkan. C # telah mendelegasikan kovariansi dan kontradiksi sejak selamanya, sehingga terasa intrinsik, tetapi VB.NET tidak memiliki kovarian dan kontradiksi yang didelegasikan hingga .NET 3.0. Oleh karena itu, VB.NET untuk .NET 2.0 dan di bawahnya tidak akan dapat menggunakan sistem ini. (Lihat contoh kode yang saya tambahkan di "Edit # 2", di atas.)
Mike Rosenblum

@Mike - Maaf, Anda 100% benar! Saya telah mengedit jawaban saya untuk mencerminkan maksud Anda :)
Andrew Hare

4
Ah, menarik! Tampaknya delegate covariance / contravariance adalah bagian dari CLR, tetapi (untuk alasan yang saya tidak tahu) tidak diekspos oleh VB.NET sebelum versi terbaru. Berikut adalah artikel oleh Francesco Balena yang menunjukkan bagaimana varians delegasi dapat dicapai dengan menggunakan Refleksi, jika tidak diaktifkan oleh bahasanya sendiri: dotnet2themax.com/blogs/fbalena/… .
Mike Rosenblum

1
@Mike - Itu selalu menarik untuk mempelajari hal-hal yang didukung CLR namun tidak didukung dalam bahasa .NET mana pun.
Andrew Hare

5

Saya mengintip bagaimana ini ditangani dengan WinRT baru dan berdasarkan pendapat lain di sini, dan akhirnya memutuskan untuk melakukannya seperti ini:

[Serializable]
public delegate void TypedEventHandler<in TSender, in TEventArgs>(
    TSender sender,
    TEventArgs e
) where TEventArgs : EventArgs;

Ini tampaknya menjadi cara terbaik untuk maju mengingat penggunaan nama TypedEventHandler di WinRT.


Mengapa menambahkan batasan umum pada TEventArgs? Itu telah dihapus dari EventHandler <> dan TypedEventHandler <,> karena tidak terlalu masuk akal.
Mike Marynowski

2

Saya pikir itu adalah ide yang bagus dan MS mungkin tidak punya waktu atau minat untuk berinvestasi dalam membuat ini lebih baik seperti misalnya ketika mereka pindah dari ArrayList ke daftar berbasis generik.


Anda mungkin benar ... Di sisi lain, menurut saya ini hanya "standar", dan mungkin bukan masalah teknis sama sekali. Artinya, kemampuan ini mungkin ada di semua bahasa .NET saat ini, saya tidak tahu. Saya tahu bahwa C # dan VB.NET dapat menangani ini. Namun, saya tidak yakin seberapa luas ini berfungsi di semua bahasa .NET saat ini ... Tetapi karena berfungsi di C # dan VB.NET, dan semua orang di sini sangat mendukung, saya pikir saya sangat mungkin melakukan ini. :-)
Mike Rosenblum

2

Dari apa yang saya pahami, bidang "Pengirim" seharusnya selalu merujuk ke objek yang menyimpan langganan acara. Jika saya memiliki druthers saya, akan ada juga bidang yang menyimpan informasi yang cukup untuk berhenti berlangganan acara jika diperlukan (*) (pertimbangkan, misalnya, logger perubahan yang berlangganan acara 'koleksi-diubah'; itu berisi dua bagian , salah satunya melakukan pekerjaan sebenarnya dan menyimpan data aktual, dan yang lainnya menyediakan pembungkus antarmuka publik, bagian utama dapat menyimpan referensi yang lemah ke bagian pembungkus. Jika bagian pembungkus dikumpulkan sampah, itu berarti tidak ada lagi orang yang tertarik dengan data yang dikumpulkan, dan logger perubahan harus berhenti berlangganan dari acara apa pun yang diterimanya).

Karena ada kemungkinan bahwa suatu objek dapat mengirim acara atas nama objek lain, saya dapat melihat beberapa kegunaan potensial untuk memiliki bidang "pengirim" yang merupakan tipe Objek, dan untuk memiliki bidang turunan EventArgs berisi referensi ke objek yang seharusnya ditindaklanjuti. Namun, kegunaan bidang "pengirim" mungkin dibatasi oleh fakta bahwa tidak ada cara yang tepat bagi objek untuk berhenti berlangganan dari pengirim yang tidak dikenal.

(*) Sebenarnya, cara yang lebih bersih untuk menangani unsubscriptions adalah dengan memiliki tipe delegasi multicast untuk fungsi yang mengembalikan Boolean; jika fungsi yang dipanggil oleh delegasi tersebut mengembalikan True, delegasi tersebut akan ditambal untuk menghapus objek itu. Ini berarti bahwa delegasi tidak lagi benar-benar tidak dapat diubah, tetapi harus dimungkinkan untuk melakukan perubahan seperti itu dengan cara yang aman untuk thread (misalnya dengan membatalkan referensi objek dan meminta kode delegasi multicast mengabaikan referensi objek null yang disematkan). Dalam skenario ini, upaya untuk mempublikasikan dan acara ke objek yang dibuang dapat ditangani dengan sangat bersih, dari mana pun acara tersebut berasal.


2

Melihat kembali penistaan ​​sebagai satu-satunya alasan untuk menjadikan pengirim sebagai tipe objek (jika untuk menghilangkan masalah dengan pelanggaran dalam kode VB 2005, yang merupakan kesalahan Microsoft IMHO), siapa pun dapat menyarankan setidaknya motif teoretis untuk memaku argumen kedua ke tipe EventArgs. Lebih jauh lagi, adakah alasan yang baik untuk menyesuaikan dengan pedoman dan konvensi Microsoft dalam kasus khusus ini?

Memiliki kebutuhan untuk mengembangkan pembungkus EventArgs lain untuk data lain yang ingin kita teruskan di dalam event handler tampak aneh, mengapa tidak bisa langsung meneruskan data itu ke sana. Pertimbangkan bagian kode berikut

[Contoh 1]

public delegate void ConnectionEventHandler(Server sender, Connection connection);

public partial class Server
{
    protected virtual void OnClientConnected(Connection connection)
    {
        if (ClientConnected != null) ClientConnected(this, connection);
    }

    public event ConnectionEventHandler ClientConnected;
}

[Contoh 2]

public delegate void ConnectionEventHandler(object sender, ConnectionEventArgs e);

public class ConnectionEventArgs : EventArgs
{
    public Connection Connection { get; private set; }

    public ConnectionEventArgs(Connection connection)
    {
        this.Connection = connection;
    }
}

public partial class Server
{
    protected virtual void OnClientConnected(Connection connection)
    {
        if (ClientConnected != null) ClientConnected(this, new ConnectionEventArgs(connection));
    }

    public event ConnectionEventHandler ClientConnected;
}

2
Ya, membuat kelas terpisah yang mewarisi dari System.EventArgs mungkin tampak tidak intuitif dan memang mewakili pekerjaan ekstra, tetapi ada alasan yang sangat bagus untuk itu. Jika Anda tidak perlu mengubah kode Anda, maka pendekatan Anda baik-baik saja. Namun kenyataannya adalah Anda mungkin perlu meningkatkan fungsionalitas acara di versi mendatang dan menambahkan properti ke argumen acara. Dalam skenario Anda, Anda harus menambahkan kelebihan beban, atau parameter opsional ke tanda tangan event handler. Ini adalah pendekatan yang digunakan di VBA dan warisan VB 6.0, yang bisa diterapkan, tetapi dalam praktiknya agak jelek.
Mike Rosenblum

1
Namun, dengan mewarisi dari EventArgs, versi yang akan datang bisa mewarisi dari kelas argumen acara lama Anda dan menambahkannya. Semua penelepon yang lebih lama masih bisa bekerja apa adanya, dengan beroperasi terhadap kelas dasar kelas argumen acara baru Anda. Sangat bersih. Lebih banyak pekerjaan untuk Anda, tetapi lebih bersih untuk penelepon yang bergantung pada perpustakaan Anda.
Mike Rosenblum

Bahkan tidak perlu mewarisinya, Anda cukup menambahkan fungsionalitas tambahan langsung ke kelas args acara Anda dan itu akan terus berfungsi dengan baik. Yang mengatakan, pembatasan pinning args ke eventargs telah dihapus karena tidak masuk akal untuk banyak skenario, yaitu. ketika Anda tahu Anda tidak perlu memperluas fungsionalitas acara tertentu atau ketika yang Anda butuhkan hanyalah argumen tipe nilai dalam aplikasi yang sangat sensitif terhadap kinerja.
Mike Marynowski

1

Dengan situasi saat ini (pengirim adalah objek), Anda dapat dengan mudah melampirkan metode ke beberapa peristiwa:

button.Click += ClickHandler;
label.Click += ClickHandler;

void ClickHandler(object sender, EventArgs e) { ... }

Jika pengirim bersifat generik, target event-klik tidak akan bertipe Button atau Label, tetapi bertipe Control (karena event tersebut didefinisikan pada Control). Jadi beberapa kejadian di kelas Tombol akan memiliki target tipe Kontrol, yang lain akan memiliki tipe target lain.


2
Tommy, Anda dapat melakukan hal yang persis sama dengan sistem yang saya usulkan. Anda masih bisa menggunakan event handler standar yang memiliki parameter 'object sender' untuk menangani event yang diketik kuat ini. (Lihat contoh kode yang sekarang saya tambahkan ke posting asli.)
Mike Rosenblum

Ya, saya setuju, ini adalah hal yang baik tentang acara .NET standar, diterima!
Lu4

1

Saya tidak berpikir ada yang salah dengan apa yang ingin Anda lakukan. Untuk sebagian besar, saya menduga bahwa object senderparameter tetap untuk terus mendukung kode 2.0 pra.

Jika Anda benar-benar ingin membuat perubahan ini untuk API publik, Anda mungkin ingin mempertimbangkan untuk membuat kelas EvenArgs dasar Anda sendiri. Sesuatu seperti ini:

public class DataEventArgs<TSender, TData> : EventArgs
{
    private readonly TSender sender, TData data;

    public DataEventArgs(TSender sender, TData data)
    {
        this.sender = sender;
        this.data = data;
    }

    public TSender Sender { get { return sender; } }
    public TData Data { get { return data; } }
}

Kemudian Anda dapat mendeklarasikan acara Anda seperti ini

public event EventHandler<DataEventArgs<MyClass, int>> SomeIndexSelected;

Dan metode seperti ini:

private void HandleSomething(object sender, EventArgs e)

akan tetap bisa berlangganan.

EDIT

Baris terakhir itu membuat saya berpikir sedikit ... Anda seharusnya dapat menerapkan apa yang Anda usulkan tanpa merusak fungsionalitas luar karena runtime tidak memiliki masalah parameter downcasting. Saya akan tetap bersandar pada DataEventArgssolusi (secara pribadi). Saya akan melakukannya, namun mengetahui bahwa itu mubazir, karena pengirim disimpan dalam parameter pertama dan sebagai properti acara args.

Salah satu manfaat tetap menggunakan DataEventArgsadalah bahwa Anda dapat merantai peristiwa, mengubah pengirim (untuk mewakili pengirim terakhir) sementara EventArgs mempertahankan pengirim asli.


Hei Michael, ini alternatif yang cukup bagus. Saya suka itu. Namun, seperti yang Anda sebutkan, adalah mubazir untuk memiliki parameter 'pengirim' yang secara efektif diteruskan dua kali. Pendekatan serupa dibahas di sini: stackoverflow.com/questions/809609/… , dan konsensus tampaknya terlalu non-standar. Inilah mengapa saya ragu-ragu untuk menyarankan ide 'pengirim' yang diketik kuat di sini. (Tampaknya diterima dengan baik, jadi saya senang.)
Mike Rosenblum

1

Lakukanlah. Untuk kode berbasis non komponen, saya sering menyederhanakan tanda tangan acara menjadi sederhana

public event Action<MyEventType> EventName

dari mana MyEventTypetidak mewarisi EventArgs. Mengapa repot, jika saya tidak pernah berniat untuk menggunakan salah satu anggota EventArgs.


1
Setuju! Mengapa kita harus merasakan diri kita monyet?
Lu4

1
+1-ed, ini yang juga saya gunakan. Terkadang kesederhanaan menang! Atau bahkan event Action<S, T>, event Action<R, S, T>dll. Saya memiliki metode ekstensi untuk Raisemereka thread-
safe
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.