.NET中的事件签名-使用强类型的“发件人”吗? - c#

我完全意识到,我提出的建议不遵循.NET准则,因此,仅鉴于此原因,可能是一个糟糕的主意。但是,我想从两个可能的角度考虑这一点:

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

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

我正在考虑使用利用强类型的“发送者”的事件签名,而不是将其键入为“对象”,这是当前的.NET设计模式。也就是说,不是使用如下所示的标准事件签名:

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

我正在考虑使用利用强类型的“发件人”参数的事件签名,如下所示:

首先,定义一个“ StrongTypedEventHandler”:

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

这与Action 并没有什么不同,但是通过使用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事件处理模式不符;但是,请记住,如果需要,可以使订户使用传统的事件处理签名:

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

就是说,如果事件处理程序需要订阅来自不同(或未知)对象类型的事件,则该处理程序可以将“ sender”参数键入“ object”,以便处理潜在的发送方对象的全部范围。

除了打破常规(我不会掉以轻心,相信我),我无法想到任何不利之处。

这里可能存在一些CLS合规性问题。这确实可以在Visual Basic .NET 2008中100%正常运行(我已经测试过),但是我认为到2005年的Visual Basic .NET的较旧版本都没有委托协方差和相反方差。 [编辑:我对此进行了测试,并确认:VB.NET 2005及以下版本无法处理此问题,但VB.NET 2008可以100%罚款。请参阅下面的“编辑#2”。]可能还有其他.NET语言对此也有问题,我不确定。

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

有人能想到这个问题吗?还是这仅仅是与惯例大相径庭,以至于让人胃口大开?

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

(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]

我对任何人和每个人的看法都感兴趣...

提前致谢,

麦克风

编辑#1:这是对 Tommy Carlier's post 的响应:

这是一个完整的工作示例,该示例显示强类型事件处理程序和使用“对象发送者”参数的当前标准事件处理程序都可以与该方法共存。您可以复制粘贴代码并运行:

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

编辑2:这是对Andrew Hare's statement关于协方差和协方差及其在此处的应用方式的回应。 C#语言的代表之间一直有协变和矛盾,以至于感觉只是“本能”,而事实并非如此。我不知道,它甚至可能是在CLR中启用的,但是直到.NET Framework 3.0(VB.NET 2008)为止,Visual Basic .NET才没有为其委托提供协方差和逆方差功能。结果,Visual Basic.NET for .NET 2.0及以下版本将无法使用此方法。

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

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可以100%正常运行。但是为了确定起见,我现在已经在VB.NET 2005上对其进行了测试,并且没有编译,并指出:

方法'公共子
SomeEventHandler(sender as Object,e

vbGenericEventHandling.GenericEventHandling.PublisherEventArgs)'
与以下签名不同
委托'代表小组
StrongTypedEventHandler(Of TSender,
TEventArgs作为System.EventArgs)(发送方
作为发布者,作为
PublisherEventArgs)”

基本上,委托在VB.NET 2005版及更低版本中是不变的。我实际上是在几年前想到这个主意的,但是VB.NET无法处理这个问题困扰了我……但是我现在已经坚定地转向C#,并且VB.NET现在可以处理它了,所以,所以这个帖子。

编辑:更新#3

好的,我已经成功使用了一段时间了。这确实是一个不错的系统。我决定将“ StrongTypedEventHandler”命名为“ GenericEventHandler”,定义如下:

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

除了这种重命名,我完全按照上面的讨论实施了它。

它确实跳过了FxCop规则CA1009,该规则指出:

“按照惯例,.NET事件有两个
指定事件的参数
发送者和事件数据。事件处理程序
签名应遵循以下格式:
void MyEventHandler(对象发送者,
EventArgs e)。 'sender'参数
始终为System.Object类型,即使
如果有可能雇用更多
具体类型。 “ e”参数是
始终为System.EventArgs类型。
不提供事件数据的事件
应该使用System.EventHandler
委托类型。事件处理程序返回
无效,以便他们可以发送每个事件
多种目标方法。任何值
目标返回的将丢失
在第一个电话之后。”

当然,我们知道所有这些,并且无论如何都在违反规则。 (在任何情况下,如果需要的话,所有事件处理程序都可以在其签名中使用标准的“对象发件人”,这是不间断的更改。)

因此,使用SuppressMessageAttribute可以达到目的:

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

我希望这种方法将来会成为标准。它确实非常好用。

感谢您的所有意见,我真的很感激...

麦克风

参考方案

微软似乎已经选择了此方法,因为现在MSDN上也有类似的示例:

Generic Delegates

.NET C#Webbrowser填充输入,不带ID或类名 - javascript

我需要在网络浏览器中填写一个输入,但这不起作用。我认为必须使用name属性来完成,但是怎么做呢?foreach (HtmlElement login in webBrowser1.Document.GetElementsByTagName("input")) { if (login.GetAttribute("name"…

ddl在服务器中未更新-asp.net - javascript

我在ASP.NET c#上工作。我有一个DropDownList。 (runat =“ server”)在$ {document).ready上,我更新了它的值:$(document).ready(function () { document.getElementById("ddl").value = "abc"; ……

asp.net mvc中的对象数组数据始终为null - javascript

我需要通过json将对象数组发送到asp.net mvc 2,但是我在mvc控制器中没有得到null对象是这样的entries[1].date = "12/22/2014" entries[1].Ref = "0002" entries[1].Credit = "100" entries[2].da…

jQuery发布不会将数据发布到ASP.NET API控制器 - javascript

我有一次噩梦般的时间通过jquery post将数据发送到ASP.NET Controller。这是JSON.stringify之后的数据:[{"scheduleTaskID":"203","task":"Permit","baselineDate":…

ASP.NET MVC中应为DataTable返回哪种数据? - javascript

我想为DataTable中的每个页面创建动态加载信息。我正在尝试遵循以下示例:https://www.datatables.net/manual/server-sidehttps://www.datatables.net/manual/data来自示例的代码:$('#example').DataTable( { serverSide: t…