.NET'te Etkinlik İmzası - Kesin Yazılmış 'Gönderen' Kullanılıyor mu?


107

Önerdiğim şeyin .NET yönergelerine uymadığının tamamen farkındayım ve bu nedenle, muhtemelen tek başına bu nedenle kötü bir fikirdir. Ancak, bunu iki olası bakış açısından değerlendirmek istiyorum:

(1) Bunu% 100 dahili amaçlar için kendi geliştirme çalışmam için kullanmayı düşünmeli miyim?

(2) Bu, çerçeve tasarımcılarının değiştirmeyi veya güncellemeyi düşünebilecekleri bir kavram mı?

Mevcut .NET tasarım modeli olan 'nesne' olarak yazmak yerine, güçlü yazılmış bir 'gönderici' kullanan bir olay imzası kullanmayı düşünüyorum. Yani, şuna benzeyen standart bir olay imzası kullanmak yerine:

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

Aşağıdaki gibi güçlü yazılmış bir "gönderen" parametresi kullanan bir etkinlik imzası kullanmayı düşünüyorum:

Önce bir "StrongTypedEventHandler" tanımlayın:

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

Bu, bir Action <TSender, TEventArgs> 'dan çok da farklı değildir, ancak bunu kullanarak StrongTypedEventHandler, TEventArgs'ın türetilmesini zorunlu kılıyoruz System.EventArgs.

Ardından, örnek olarak StrongTypedEventHandler'ı aşağıdaki gibi bir yayınlama sınıfında kullanabiliriz:

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

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

Yukarıdaki düzenleme, abonelerin, döküm gerektirmeyen güçlü tipte bir olay işleyicisini kullanmasına olanak tanır:

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

Bunun standart .NET olay işleme modelinden tamamen koptuğunun farkındayım; ancak, kontravaryansın bir abonenin istenirse geleneksel bir olay işleme imzasını kullanmasını sağlayacağını unutmayın:

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

Yani, bir olay işleyicisinin farklı (veya belki bilinmeyen) nesne türlerinden gelen olaylara abone olması gerekiyorsa, işleyici, potansiyel gönderici nesnelerinin tüm genişliğini işlemek için "gönderen" parametresini "nesne" olarak yazabilir.

Konvansiyonu bozmaktan başka (ki bu hafife almadığım bir şey, inanın bana) bunda herhangi bir olumsuzluk düşünemiyorum.

Burada bazı CLS uyum sorunları olabilir. Bu, Visual Basic .NET 2008'de% 100 iyi çalışmaktadır (test ettim), ancak Visual Basic .NET'in 2005'e kadar olan eski sürümlerinde temsilci kovaryansı ve kontraveri olmadığına inanıyorum. [Düzenleme: O zamandan beri bunu test ettim ve onaylandı: VB.NET 2005 ve altı bunu kaldıramaz, ancak VB.NET 2008% 100 iyi. Aşağıdaki "Düzenleme # 2" ye bakın.] Bununla ilgili bir sorunu olan başka .NET dilleri de olabilir, emin olamıyorum.

Ancak kendimi C # veya Visual Basic .NET dışında herhangi bir dil için geliştirdiğimi görmüyorum ve bunu .NET Framework 3.0 ve üzeri için C # ve VB.NET ile sınırlandırmaktan çekinmiyorum. (Dürüst olmak gerekirse, bu noktada 2.0'a geri dönmeyi hayal bile edemezdim.)

Başka biri bununla ilgili bir sorun düşünebilir mi? Yoksa bu basitçe gelenekleri o kadar çok bozuyor ki insanların midesini döndürüyor mu?

İşte bulduğum bazı ilgili bağlantılar:

(1) Etkinlik Tasarım Yönergeleri [MSDN 3.5]

(2) C # basit Event Raising - "gönderen" ve özel EventArgs kullanarak [StackOverflow 2009]

(3) .net'te olay imza kalıbı [StackOverflow 2008]

Herkesin ve herkesin bu konudaki fikirleriyle ilgileniyorum ...

Şimdiden teşekkürler,

Mike

Düzenleme # 1: Bu, Tommy Carlier'ın gönderisine yanıt olarak :

Burada, hem kesin tipli olay işleyicilerin hem de 'nesne gönderen' parametresi kullanan geçerli standart olay işleyicilerin bu yaklaşımla bir arada var olabileceğini gösteren eksiksiz bir çalışma örneği verilmiştir. Kodu kopyalayıp yapıştırabilir ve çalıştırabilirsiniz:

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

Düzenleme # 2: Bu, Andrew Hare'nin kovaryans ve kontravans ve burada nasıl geçerli olduğuna ilişkin açıklamasına bir yanıttır . C # dilindeki delegeler, o kadar uzun süredir kovaryans ve kontravere sahipler ki, bu sadece "içsel" hissediyor, ama değil. CLR'de etkinleştirilmiş bir şey bile olabilir, bilmiyorum, ancak Visual Basic .NET, .NET Framework 3.0'a (VB.NET 2008) kadar temsilciler için kovaryans ve kontravans kabiliyeti elde edemedi. Ve sonuç olarak, .NET 2.0 için Visual Basic.NET ve aşağısı bu yaklaşımı kullanamayacaktır.

Örneğin, yukarıdaki örnek aşağıdaki gibi VB.NET'e çevrilebilir:

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 onu% 100 iyi çalıştırabilir. Ama şimdi emin olmak için VB.NET 2005'te test ettim ve derlemiyor, şunu belirtiyor:

Yöntem 'Public Sub SomeEventHandler (gönderen Nesne, e vbGenericEventHandling.GenericEventHandling.PublisherEventArgs olarak)' temsilci ile aynı imzaya sahip değil 'Delege Sub StrongTypedEventHandler (TSender, TEventArgs As System.EventArgs) (Gönderen Yayımcı, e '

Temel olarak, temsilciler VB.NET'in 2005 ve daha önceki sürümlerinde değişmez. Aslında bu fikri birkaç yıl önce düşünmüştüm, ancak VB.NET'in bununla başa çıkamaması beni rahatsız etti ... Ama şimdi sağlam bir şekilde C #'a geçtim ve VB.NET artık bunu halledebilir, bu yüzden, iyi, bu yüzden bu gönderi.

Düzenleme: 3 numaralı güncelleme

Tamam, bir süredir bunu oldukça başarılı bir şekilde kullanıyorum. Gerçekten güzel bir sistem. "StrongTypedEventHandler" cihazımı aşağıdaki gibi "GenericEventHandler" olarak adlandırmaya karar verdim:

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

Bu yeniden adlandırmanın dışında, tam olarak yukarıda tartışıldığı gibi uyguladım.

FxCop kuralı CA1009'a göre hareket eder ve şunları belirtir:

"Kural olarak, .NET olaylarının olay gönderenini ve olay verilerini belirten iki parametresi vardır. Olay işleyici imzaları şu biçimi izlemelidir: void MyEventHandler (nesne gönderen, EventArgs e)." Gönderen "parametresi her zaman System.Object tipindedir, daha spesifik bir tür kullanmak mümkün olsa bile. 'e' parametresi her zaman System.EventArgs tipindedir. Olay verileri sağlamayan olaylar System.EventHandler temsilci türünü kullanmalıdır. Olay işleyicileri, gönderebilmeleri için void döndürür her olay birden çok hedef yönteme. Bir hedef tarafından döndürülen herhangi bir değer ilk çağrıdan sonra kaybolur. "

Elbette tüm bunları biliyoruz ve yine de kuralları çiğniyoruz. (Herhangi bir durumda tercih edilirse, tüm olay işleyicileri imzalarında standart 'nesne Gönderen'i kullanabilir - bu, kesintisiz bir değişikliktir.)

Öyleyse, a kullanımı SuppressMessageAttributehile yapar:

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

Umarım bu yaklaşım gelecekte bir noktada standart haline gelir. Gerçekten çok güzel çalışıyor.

Tüm görüşleriniz için teşekkürler beyler, gerçekten minnettarım ...

Mike


6
Yap. (Bunun bir cevabı haklı çıkaracağını düşünmeyin.)
Konrad Rudolph

1
Benim iddialarım size pek işaret edilmedi: elbette bunu kendi projelerinizde yapmalısınız. BCL'de neden işe yaramayacağına dair argümanlardı.
Tommy Carlier

3
Dostum, keşke projem bunu baştan yapmış olsaydı, göndereni seçmekten nefret ediyorum.
Matt H

7
Şimdi BU bir sorudur. Gördünüz mü millet? Bu tweet boyutundaki oh hi this my hom work solve it plz :code dump:sorulardan biri değil , öğrendiğimiz bir soru .
Camilo Martin

3
Başka bir öneri, sadece isim EventHandler<,>daha GenericEventHandler<,>. EventHandler<>BCL'de yalnızca EventHandler adlı jenerik var . Yani EventHandler daha yaygın bir isimdir ve delegeler tip aşırı yüklemelerini destekler
nawfal

Yanıtlar:


25

Görünüşe göre Microsoft, benzer bir örnek şimdi MSDN'de olduğu için bunu anladı:

Genel Temsilciler


2
+1 Ah, harika. Gerçekten bunu anladılar. Bu iyi. Yine de, bunu VS IDE içinde tanınmış bir model haline getirdiklerini umuyorum, çünkü şu anda olduğu gibi, IntelliSense vb. Açısından bu modeli kullanmak daha garip.
Mike Rosenblum

13

Önerdiğiniz şey aslında çok mantıklı ve merak ediyorum bu basitçe olan şeylerden biri mi, çünkü orijinal olarak jeneriklerden önce tasarlandı mı yoksa bunun gerçek bir nedeni mi var?


1
Eminim sebebi budur. Bununla birlikte, artık dilin yeni sürümleri bunun üstesinden gelmek için kontravarlığa sahip olduklarından, bunu geriye dönük bir şekilde halledebilmeleri gerekiyor gibi görünüyor. Bir 'gönderen nesnesi' kullanan önceki işleyiciler bozulmaz. Ancak bu eski diller için doğru değildir ve bazı güncel .NET dilleri için doğru olmayabilir, emin değilim.
Mike Rosenblum

13

Windows Çalışma Zamanı (WinRT) TypedEventHandler<TSender, TResult>, sizin yaptığınız şeyi tam olarak yapan StrongTypedEventHandler<TSender, TResult>, ancak görünüşe göre TResulttür parametresinde kısıtlama olmadan bir temsilci sunar :

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

MSDN belgeleri burada .


1
Ah, bir ilerleme olduğunu görmek güzel ... TResult'un neden 'EventArgs' sınıfından miras almakla sınırlı olmadığını merak ediyorum. 'EventArgs' temel sınıfı temelde boştur; belki bu kısıtlamadan uzaklaşıyorlar?
Mike Rosenblum

Tasarım ekibinin bir denetimi olabilir; kim bilir.
Pierre Arnaud

olaylar kullanılmadan iyi işliyor EventArgs, bu sadece bir kongre meselesi
Sebastian

3
Bu özellikle bu TypedEventHandler belgelerinde devletler argsolacak nullhiçbir olay verisi olup olmadığını onlar uzakta varsayılan olarak esasen boş bir nesne kullanarak elde edilir ki görünmüyor böylece. Tahminimce orijinal fikir, ikinci bir tip parametresine sahip bir yöntemin EventArgsherhangi bir olayı işleyebileceğiydi çünkü türler her zaman uyumlu olurdu. Muhtemelen şimdi birden çok farklı olayı tek bir yöntemle ele almanın o kadar da önemli olmadığını anlıyorlar.
jmcilhinney

1
Gözden kaçmış gibi görünmüyor. Kısıtlama System.EventHandler <TEventArgs> temsilcisinden de kaldırıldı. reference.microsoft.com/#mscorlib/system/…
colton7909

5

Aşağıdaki ifadelere itiraz ediyorum:

  • Visual Basic .NET'in 2005 yılına kadar olan eski sürümlerinde temsilci kovaryansı ve kontraveri olmadığına inanıyorum.
  • Bunun küfürle sonuçlandığının tamamen farkındayım.

Her şeyden önce, burada yaptığınız hiçbir şeyin kovaryans veya kontravans ile bir ilgisi yok. ( Düzenleme: Bir önceki açıklamada, yanlış bakın Daha fazla bilgi için Delegeler de Kovaryansı ve contravariance ) Bu çözüm, tüm CLR sürümleri 2.0 ve üstü sadece iyi çalışır (Açıkçası bu irade değil bir CLR 1.0 uygulamasında çalışmaları bunun generics kullanır gibi).

İkincisi, bu harika bir fikir olduğu için fikrinizin “küfür” e ulaştığına kesinlikle katılmıyorum.


2
Merhaba Andrew, beğendiğin için teşekkürler! İtibar seviyeniz düşünüldüğünde, bu benim için gerçekten çok şey ifade ediyor ... Kovaryans / kontraverans konusunda: abone tarafından sağlanan delege, yayıncının etkinliğinin imzasıyla tam olarak eşleşmiyorsa, kovaryans ve kontravarlık söz konusudur. C # sonsuza dek temsilci kovaryansına ve kontravansına sahipti, bu yüzden içsel hissediyor, ancak VB.NET, .NET 3.0'a kadar temsilci kovaryans ve kontravansına sahip değildi. Bu nedenle, .NET 2.0 ve altı için VB.NET bu sistemi kullanamayacaktır. (Yukarıdaki "Düzenleme # 2" ye eklediğim kod örneğine bakın.)
Mike Rosenblum

@Mike - Özür dilerim,% 100 haklısın!
Andrew Hare

4
Ah, ilginç! Temsilci kovaryansı / kontraveri CLR'nin bir parçası gibi görünüyor, ancak (bilmediğim nedenlerle) en son sürümden önce VB.NET tarafından ifşa edilmedi. Dilin kendisi tarafından etkinleştirilmemişse, temsilci varyansının Yansıma kullanılarak nasıl elde edilebileceğini gösteren bir Francesco Balena makalesi: dotnet2themax.com/blogs/fbalena/… .
Mike Rosenblum

1
@Mike - CLR'nin desteklediği ancak .NET dillerinin hiçbirinde desteklenmeyen şeyleri öğrenmek her zaman ilginçtir.
Andrew Hare

5

Bunun yeni WinRT ile nasıl ele alındığına ve buradaki diğer fikirlere dayanarak bir göz attım ve sonunda şu şekilde yapmaya karar verdim:

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

Bu, WinRT'de TypedEventHandler adının kullanımı düşünüldüğünde en iyi yol gibi görünüyor.


TEventArgs'a neden genel kısıtlama eklemelisiniz? EventHandler <> ve TypedEventHandler <,> 'den kaldırıldı çünkü gerçekten mantıklı değildi.
Mike Marynowski

2

Bunun harika bir fikir olduğunu düşünüyorum ve MS, örneğin ArrayList'ten jenerik tabanlı listelere geçtiklerinde, bunu daha iyi hale getirmek için yatırım yapacak zamanı veya ilgisi olmayabilir.


Haklı olabilirsin ... Öte yandan, bunun sadece bir "standart" olduğunu ve muhtemelen hiç teknik bir sorun olmadığını düşünüyorum. Yani, bu yetenek tüm güncel .NET dillerinde mevcut olabilir, bilmiyorum. C # ve VB.NET'in bunu halledebileceğini biliyorum. Ancak, bunun tüm mevcut .NET dillerinde ne kadar geniş çapta çalıştığından emin değilim ... Ama C # ve VB.NET'te çalıştığı ve buradaki herkes çok destekleyici olduğu için, bunu yapabileceğimi düşünüyorum. :-)
Mike Rosenblum

2

Anladığım kadarıyla, "Gönderen" alanının her zaman olay aboneliğini tutan nesneye atıfta bulunması gerekiyor. İlaçlarım olsaydı, gerekli olması halinde bir olayın aboneliğini iptal etmek için yeterli bilgileri tutan bir alan da olurdu (*) (örneğin, 'koleksiyon-değiştirilen' olaylara abone olan bir değişiklik günlüğünü düşünün; iki bölüm içerir , biri asıl işi yapan ve gerçek verileri tutan ve diğeri bir genel arayüz sarıcı sağlayan, ana parça sarıcı kısmına zayıf bir referans tutabilir. Sarıcı kısmı çöp olarak toplanırsa, bu demektir artık toplanan verilerle ilgilenen kimse yoktu ve bu nedenle değişiklik kaydedici aldığı herhangi bir olayın aboneliğini iptal etmelidir).

Bir nesnenin başka bir nesne adına olaylar göndermesi mümkün olduğundan, Nesne türünde bir "gönderen" alanına sahip olmak ve EventArgs-türetilmiş alanın, olması gereken nesneye bir başvuru içermesi için bazı olası faydalar görebiliyorum. harekete geçmek. Bununla birlikte, "gönderen" alanının kullanışlılığı, muhtemelen, bir nesnenin bilinmeyen bir göndericiden aboneliğini iptal etmesi için temiz bir yol olmadığı gerçeğiyle sınırlıdır.

(*) Aslında, abonelik iptallerini işlemenin daha net bir yolu, Boolean döndüren işlevler için çok noktaya yayın delege türüne sahip olmaktır; böyle bir temsilci tarafından çağrılan bir işlev True döndürürse, temsilciye bu nesneyi kaldırmak için yama uygulanacaktır. Bu, delegelerin artık gerçekten değişmez olmayacağı anlamına gelir, ancak bu tür bir değişikliği iş parçacığı güvenli bir şekilde gerçekleştirmenin mümkün olması gerekir (örneğin, nesne referansını geçersiz kılarak ve çok noktaya yayın delege kodunun gömülü boş nesne referanslarını göz ardı etmesini sağlayarak). Bu senaryoya göre, elden çıkarılan bir nesneye yayınlama girişimi ve olay, olay nereden gelirse gelsin çok temiz bir şekilde ele alınabilir.


2

Göndereni bir nesne türü haline getirmenin tek nedeni olarak küfüre dönüp baktığımızda (eğer Microsoft'un hata IMHO'su olan VB 2005 kodundaki kontravaryans ile ilgili sorunları göz ardı edersek), herhangi biri en azından EventArgs türüne ikinci argümanı çivilemek için teorik bir neden önerebilir. Daha da ileri gidersek, bu özel durumda Microsoft'un yönergelerine ve kurallarına uymak için iyi bir neden var mı?

Olay işleyicisinin içine aktarmak istediğimiz başka bir veri için başka bir EventArgs sarıcı geliştirmeye ihtiyaç duymak garip görünüyor, neden bu verileri oraya doğrudan aktaramıyoruz? Aşağıdaki kod bölümlerini düşünün

[Örnek 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;
}

[Örnek 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
Evet, System.EventArgs'dan miras alan ayrı bir sınıf oluşturmak sezgisel görünebilir ve fazladan çalışmayı temsil eder, ancak bunun çok iyi bir nedeni vardır. Kodunuzu hiçbir zaman değiştirmeniz gerekmiyorsa, yaklaşımınız iyidir. Ancak gerçek şu ki, gelecekteki bir sürümde olayın işlevselliğini artırmanız ve olay değişkenlerine özellikler eklemeniz gerekebilir. Senaryonuzda, olay işleyicisinin imzasına fazladan aşırı yükler veya isteğe bağlı parametreler eklemeniz gerekir. Bu, uygulanabilir ancak pratikte biraz çirkin olan, VBA ve eski VB 6.0'da kullanılan bir yaklaşımdır.
Mike Rosenblum

1
Bununla birlikte, EventArgs'dan miras alarak, gelecekteki bir sürüm eski olay bağımsız değişkenleri sınıfınızdan miras alabilir ve onu artırabilir. Tüm eski arayanlar, yeni olay bağımsız değişkenleri sınıfınızın temel sınıfına karşı çalışarak aynen olduğu gibi çalışabilir. Çok temiz. Sizin için daha fazla iş, ancak kitaplığınıza bağlı olan tüm arayanlar için daha temiz.
Mike Rosenblum

Onu devralması bile gerekmez, sadece ek işlevselliği doğrudan olay args sınıfınıza ekleyebilirsiniz ve düzgün çalışmaya devam edecektir. Bununla birlikte, argümanları olay argümanlarına sabitleme kısıtlaması kaldırıldı çünkü birçok senaryo için pek bir anlam ifade etmiyordu, yani. Belirli bir olayın işlevselliğini asla genişletmeniz gerekmeyeceğini bildiğinizde veya ihtiyacınız olan tek şey performansa duyarlı uygulamalarda argüman olan bir değer türü olduğunda.
Mike Marynowski

1

Mevcut durumla (gönderen nesnedir), birden çok olaya kolayca bir yöntem ekleyebilirsiniz:

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

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

Gönderen jenerik ise, tıklama olayının hedefi Düğme veya Etiket türünde değil, Control türünde olacaktır (çünkü olay Control'de tanımlanmıştır). Dolayısıyla, Button sınıfındaki bazı olayların Control türü bir hedefi olurken, diğerlerinin başka hedef türleri olabilir.


2
Tommy, yapabileceğiniz tam olarak ben teklif ediyorum o sistemle bu aynı şey. Bu kesin tipli olayları işlemek için bir 'nesne gönderen' parametresine sahip standart bir olay işleyicisi kullanmaya devam edebilirsiniz. (Orijinal
gönderiye

Evet, kabul ediyorum, standart .NET etkinlikleri hakkında iyi bir şey kabul edildi!
Lu4

1

Yapmak istediğin şeyde yanlış bir şey olduğunu sanmıyorum. Çoğunlukla, object sender2.0 öncesi kodu desteklemeye devam etmek için parametrenin kaldığından şüpheleniyorum .

Bu değişikliği genel bir API için gerçekten yapmak istiyorsanız, kendi temel EvenArgs sınıfınızı oluşturmayı düşünebilirsiniz. Bunun gibi bir şey:

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

O zaman olaylarını böyle ilan edebilirsin

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

Ve bunun gibi yöntemler:

private void HandleSomething(object sender, EventArgs e)

yine de abone olabilecek.

DÜZENLE

Bu son satır beni biraz düşündürdü ... Çalışma zamanında herhangi bir sorun aşağı değerlendirme parametreleri olmadığından, önerdiğiniz şeyi hiçbir dış işlevselliği bozmadan uygulayabilmelisiniz. Yine de DataEventArgsçözüme (kişisel olarak) eğilirdim . Gönderen ilk parametrede ve olay değişkenlerinin bir özelliği olarak saklandığından, bunun gereksiz olduğunu bilerek bunu yapardım.

Buna bağlı kalmanın bir yararı DataEventArgs, EventArgs orijinal göndereni korurken, göndereni değiştirerek (son göndereni temsil etmek için) olayları zincirleyebilmenizdir.


Hey Michael, bu oldukça güzel bir alternatif. Bunu sevdim. Yine de belirttiğiniz gibi, 'gönderen' parametresinin etkin bir şekilde iki kez geçirilmesi gereksizdir. Benzer bir yaklaşım burada tartışılmaktadır: stackoverflow.com/questions/809609/… ve fikir birliği çok standart dışı olduğu yönünde. Bu yüzden burada güçlü tipte 'gönderici' fikrini önermekte tereddüt ettim. (Yine de iyi karşılanmış gibi görünüyor, bu yüzden memnunum.)
Mike Rosenblum

1

Göreyim seni. Bileşen tabanlı olmayan kod için, Etkinlik imzalarını genellikle

public event Action<MyEventType> EventName

nereden MyEventTypemiras kalmaz EventArgs. EventArgs üyelerinden herhangi birini kullanmak istemiyorsam neden rahatsız edeyim?


1
Katılıyorum! Neden kendimizi maymunlar gibi hissetmeliyiz?
Lu4

1
+ 1-ed, ben de bunu kullanıyorum. Bazen basitlik kazanır! Hatta event Action<S, T>, event Action<R, S, T>vb. RaiseOnlara güvenli bir şekilde bir uzatma yöntemim var :)
nawfal
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.