.NET中的事件签名 - 使用强类型的“发件人”? [英] Event Signature in .NET -- Using a Strong Typed 'Sender'?

查看:87
本文介绍了.NET中的事件签名 - 使用强类型的“发件人”?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我完全意识到,我提出的并不遵循.NET指导原则,因此,这个原因可能是一个糟糕的想法。但是,我想从两个可能的角度来考虑这一点:



(1)我应该考虑将这个用于我自己的开发工作,这是100%的内部目的。



(2)这是框架设计师可以考虑更改或更新的一个概念吗?



我在想关于使用一个使用强类型发件人的事件签名,而不是将其键入为对象,这是当前的.NET设计模式。也就是说,而不是使用如下的标准事件签名:

  class Publisher 
{
公共事件EventHandler< PublisherEventArgs> SomeEvent;
}

我正在考虑使用一个使用强类型发件人的事件签名参数如下:



首先,定义一个StrongTypedEventHandler:

  [SerializableAttribute] 
public delegate void StrongTypedEventHandler< TSender,TEventArgs>(
TSender sender,
TEventArgs e

其中TEventArgs:EventArgs;

这与Action< TSender,TEventArgs>不同,但是通过使用 StrongTypedEventHandler ,我们强制TEventArgs派生自 System.EventArgs



接下来,作为一个例子,我们可以在发布类中使用StrongTypedEventHandler,如下所示:

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

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

上述安排将使订阅者利用不需要转换的强类型事件处理程序:

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

我完全意识到这与标准的.NET事件处理模式但是,请注意,如果需要,违反将使订阅者能够使用传统的事件处理签名:

 类订阅者
{
void SomeEventHandler(object sender,PublisherEventArgs e)
{
if(((Publisher)sender).Name ==John Smith)
{
// ...
}
}
}

是,如果事件处理程序需要从不同(或可能未知)的对象类型订阅事件,则处理程序可以将'sender'参数键入为'object',以便处理潜在的发送方对象的全部广告。



除了打破会议(这是我不轻易的,相信我),我不能想到任何缺点。



这里可能存在一些CLS合规性问题。这在Visual Basic .NET 2008中运行100%(我已经测试过),但我相信,旧版本的Visual Basic .NET到2005没有委托协方差和逆向。 可能还有其他.NET语言也有问题,我无法确定。



但是我看不到自己开发C#或Visual Basic .NET以外的任何语言,我不介意将它限制在.NET Framework 3.0及更高版本的C#和VB.NET中。 (我无法想象,现在回到2.0,说实话。)



任何人都可以想到这个问题吗?或者这样做简单地违反了惯例,让人们的肚子转过来吗?



以下是我找到的一些相关链接:



(1)活动设计指南[MSDN 3.5]



(2) C#简单事件提升 - 使用发件人与自定义EventArgs [StackOverflow 2009]



(3) .net中的事件签名模式[StackOverflow 2008]



我感兴趣的是任何人和每个人对此的意见...



提前感谢



Mike



编辑#1:这是针对 Tommy Carlier的帖子



这是一个完整的工作示例,显示了强类型的事件处理程序和当前的标准事件处理程序使用object sender参数可以与这种方法共存。您可以在代码中复制粘贴并运行:

 命名空间csScrap.GenericEventHandling 
{
class PublisherEventArgs:EventArgs
{
// ...
}

[SerializableAttribute]
public delegate void StrongTypedEventHandler< TSender,TEventArgs>(
TSender发件人,
TEventArgs e

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

编辑#2 :这是为了回应安德鲁·哈雷关于协方差和相互矛盾的陈述,以及它在这里的适用。 C#语言的代表已经有了协调和矛盾的时间,只是感觉内在,但并不是这样。甚至可能是在CLR中启用的东西,我不知道,但Visual Basic .NET在.NET Framework 3.0(VB.NET 2008)之前没有获得代理的协方差和逆变能力。因此,Visual Basic.NET for .NET 2.0及以下版本将无法使用此方法。



例如,上述示例可以转换为VB .NET如下:

 命名空间GenericEventHandling 
类PublisherEventArgs
继承EventArgs
'..
'...
结束类

< SerializableAttribute()> _
公共委托Sub StrongTypedEventHandler(来自TSender,TEventArgs As EventArgs)_
(ByVal sender As TSender,ByVal e As TEventArgs)

类发布者
公共事件SomeEvent作为StrongTypedEventHandler(Of Publisher,PublisherEventArgs)

Public Sub OnSomeEvent()
RaiseEvent SomeEvent(Me,New PublisherEventArgs)
End Sub
结束类

类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。)
结束Sub
结束类

类测试者
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
结束类
结束命名空间

VB.NET 2008可以运行100%的罚款。但是现在我已经在VB.NET 2005上测试了,只是为了确定,并没有编译,说:


方法' Public Sub
SomeEventHandler(sender As Object,e
As
vbGenericEventHandling.GenericEventHandling.PublisherEventArgs)'

委托没有相同的签名'Delegate Sub
StrongTypedEventHandler(Of TSender,
TEventArgs As System.EventArgs)(sender
As Publisher,e As
PublisherEventArgs)'


基本上,代表在2005年及以后的VB.NET版本中是不变的。几年前我实际上想到了这个想法,但是VB.NET无法应对这个困扰我...但是我现在已经坚定地移动到C#,VB.NET现在可以处理它,所以,因此这个帖子。



编辑:更新#3



使用这个相当成功一段时间了。这真的是一个不错的系统。我决定将我的StrongTypedEventHandler命名为GenericEventHandler,定义如下:

  [SerializableAttribute] 
public delegate void GenericEventHandler< TSender,TEventArgs>(
TSender sender,
TEventArgs e

其中TEventArgs:EventArgs;

除了重命名之外,我完全按照上述方法实现了它。



它通过FxCop规则CA1009执行,它指出:


按照惯例,.NET事件有两个
参数指定事件
发件人和事件数据事件处理程序
签名应遵循以下格式:
void MyEventHandler(object sender,
EventArgs e)。'sender '参数
始终是System.Object类型,即使
,如果可以使用更多的
特定类型,'e'参数是
,始终是System.EventArgs
不提供事件数据
的事件应该使用System.EventHandler
委托类型。事件处理程序返回
void,以便它们可以将每个事件
发送到多个目标方法,由目标返回的任何值
将在首次调用后将丢失


当然,我们知道所有这一切,并正在违反规则。 (所有事件处理程序都可以在其签名中使用标准的对象发件人,如果是任何情况下都是首选的 - 这是一个不间断的变化。)



所以使用一个 SuppressMessageAttribute 做的诀窍:

  [SuppressMessage(Microsoft.Design ,CA1009:DeclareEventHandlersCorrectly,
对齐=使用强类型的GenericEventHandler< TSender,TEventArgs>事件处理程序模式)]

我希望这个方法在将来的某个时候成为标准。感谢您的意见,我非常感谢...



迈克

解决方案

似乎微软已经接受了这个类似的例子,现在在MSDN上:



通用代表


I fully realize that what I am proposing does not follow the .NET guidelines, and, therefore, is probably a poor idea for this reason alone. However, I would like to consider this from two possible perspectives:

(1) Should I consider using this for my own development work, which is 100% for internal purposes.

(2) Is this a concept that the framework designers could consider changing or updating?

I am thinking about using an event signature that utilizes a strong typed 'sender', instead of typing it as 'object', which is the current .NET design pattern. That is, instead of using a standard event signature that looks like this:

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

I am considering using an event signature that utilizes a strong-typed 'sender' parameter, as follows:

First, define a "StrongTypedEventHandler":

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

This is not all that different from an Action<TSender, TEventArgs>, but by making use of the StrongTypedEventHandler, we enforce that the TEventArgs derives from System.EventArgs.

Next, as an example, we can make use of the StrongTypedEventHandler in a publishing class as follows:

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

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

The above arrangement would enable subscribers to utilize a strong-typed event handler that did not require casting:

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

I do fully realize that this breaks with the standard .NET event-handling pattern; however, keep in mind that contravariance would enable a subscriber to use a traditional event handling signature if desired:

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

That is, if an event handler needed to subscribe to events from disparate (or perhaps unknown) object types, the handler could type the 'sender' parameter as 'object' in order to handle the full breadth of potential sender objects.

Other than breaking convention (which is something that I do not take lightly, believe me) I cannot think of any downsides to this.

There may be some CLS compliance issues here. This does run in Visual Basic .NET 2008 100% fine (I've tested), but I believe that the older versions of Visual Basic .NET through 2005 do not have delegate covariance and contravariance. [Edit: I have since tested this, and it is confirmed: VB.NET 2005 and below cannot handle this, but VB.NET 2008 is 100% fine. See "Edit #2", below.] There may be other .NET languages that also have a problem with this, I can't be sure.

But I do not see myself developing for any language other than C# or Visual Basic .NET, and I do not mind restricting it to C# and VB.NET for .NET Framework 3.0 and above. (I could not imagine going back to 2.0 at this point, to be honest.)

Can anyone else think of a problem with this? Or does this simply break with convention so much that it makes people's stomachs turn?

Here are some related links that I've found:

(1) Event Design Guidelines [MSDN 3.5]

(2) C# simple Event Raising - using "sender" vs. custom EventArgs [StackOverflow 2009]

(3) Event signature pattern in .net [StackOverflow 2008]

I am interested in anyone's and everyone's opinion on this...

Thanks in advance,

Mike

Edit #1: This is in response to Tommy Carlier's post :

Here's a full working example that shows that both strong-typed event handlers and the current standard event handlers that use a 'object sender' parameter can co-exist with this approach. You can copy-paste in the code and give it a run:

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

Edit #2: This is in response to Andrew Hare's statement regarding covariance and contravariance and how it applies here. Delegates in the C# language have had covariance and contravariance for so long that it just feels "intrinsic", but it's not. It might even be something that is enabled in the CLR, I don't know, but Visual Basic .NET did not get covariance and contravariance capability for its delegates until the .NET Framework 3.0 (VB.NET 2008). And as a result, Visual Basic.NET for .NET 2.0 and below would not be able to utilize this approach.

For example, the above example can be translated into VB.NET as follows:

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 can run it 100% fine. But I've now tested it on VB.NET 2005, just to be sure, and it does not compile, stating:

Method 'Public Sub SomeEventHandler(sender As Object, e As vbGenericEventHandling.GenericEventHandling.PublisherEventArgs)' does not have the same signature as delegate 'Delegate Sub StrongTypedEventHandler(Of TSender, TEventArgs As System.EventArgs)(sender As Publisher, e As PublisherEventArgs)'

Basically, delegates are invariant in VB.NET versions 2005 and below. I actually thought of this idea a couple of years ago, but VB.NET's inability to deal with this bothered me... But I've now moved solidly to C#, and VB.NET can now handle it, so, well, hence this post.

Edit: Update #3

Ok, I have been using this quite successfully for a while now. It really is a nice system. I decided to name my "StrongTypedEventHandler" as "GenericEventHandler", defined as follows:

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

Other than this renaming, I implemented it exactly as discussed above.

It does trip over FxCop rule CA1009, which states:

"By convention, .NET events have two parameters that specify the event sender and event data. Event handler signatures should follow this form: void MyEventHandler( object sender, EventArgs e). The 'sender' parameter is always of type System.Object, even if it is possible to employ a more specific type. The 'e' parameter is always of type System.EventArgs. Events that do not provide event data should use the System.EventHandler delegate type. Event handlers return void so that they can send each event to multiple target methods. Any value returned by a target would be lost after the first call."

Of course, we know all this, and are breaking the rules anyway. (All event handlers can use the standard 'object Sender' in their signature if preferred in any case -- this is a non-breaking change.)

So the use of a SuppressMessageAttribute does the trick:

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

I hope that this approach becomes the standard at some point in the future. It really works very nicely.

Thanks for all your opinions guys, I really appreciate it...

Mike

解决方案

It seems Microsoft has picked up on this as a similar example is now on MSDN:

Generic Delegates

这篇关于.NET中的事件签名 - 使用强类型的“发件人”?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆