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
oh hi this my hom work solve it plz :code dump:
pertanyaan berukuran tweet ini , tetapi pertanyaan yang kami pelajari .EventHandler<,>
dariGenericEventHandler<,>
. Sudah ada generikEventHandler<>
di BCL yang dinamai hanya EventHandler. Jadi EventHandler adalah nama yang lebih umum dan delegasi mendukung kelebihan tipeJawaban:
Tampaknya Microsoft telah mengambil ini karena contoh serupa sekarang ada di MSDN:
Delegasi Umum
sumber
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.
sumber
Windows Runtime (WinRT) memperkenalkan
TypedEventHandler<TSender, TResult>
delegasi, yang melakukan apa yang AndaStrongTypedEventHandler<TSender, TResult>
lakukan, tetapi tampaknya tanpa batasan padaTResult
parameter tipe:Dokumentasi MSDN ada di sini .
sumber
EventArgs
, itu hanya hal konvensiargs
akan menjadinull
jika 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 keduaEventArgs
dapat 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.Saya mempermasalahkan pernyataan berikut:
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.
sumber
Saya mengintip bagaimana ini ditangani dengan WinRT baru dan berdasarkan pendapat lain di sini, dan akhirnya memutuskan untuk melakukannya seperti ini:
Ini tampaknya menjadi cara terbaik untuk maju mengingat penggunaan nama TypedEventHandler di WinRT.
sumber
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.
sumber
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.
sumber
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]
[Contoh 2]
sumber
Dengan situasi saat ini (pengirim adalah objek), Anda dapat dengan mudah melampirkan metode ke beberapa peristiwa:
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.
sumber
Saya tidak berpikir ada yang salah dengan apa yang ingin Anda lakukan. Untuk sebagian besar, saya menduga bahwa
object sender
parameter 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:
Kemudian Anda dapat mendeklarasikan acara Anda seperti ini
Dan metode seperti ini:
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
DataEventArgs
solusi (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
DataEventArgs
adalah bahwa Anda dapat merantai peristiwa, mengubah pengirim (untuk mewakili pengirim terakhir) sementara EventArgs mempertahankan pengirim asli.sumber
Lakukanlah. Untuk kode berbasis non komponen, saya sering menyederhanakan tanda tangan acara menjadi sederhana
dari mana
MyEventType
tidak mewarisiEventArgs
. Mengapa repot, jika saya tidak pernah berniat untuk menggunakan salah satu anggota EventArgs.sumber
event Action<S, T>
,event Action<R, S, T>
dll. Saya memiliki metode ekstensi untukRaise
mereka thread-