c# - .NET 中的事件签名 - 使用强类型 'Sender' 吗?

标签 c# .net vb.net events

关闭。这个问题是opinion-based .它目前不接受答案。












想改善这个问题吗?更新问题,以便可以通过 editing this post 用事实和引文回答问题.

12 个月前关闭。



Improve this question




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

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

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

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

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

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

首先,定义一个“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")
        {
            // ...
        }
    }
}

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

除了打破惯例(这是我不会掉以轻心的事情,相信我)我想不出任何缺点。

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

但我不认为自己为 C# 或 Visual Basic .NET 以外的任何语言进行开发,而且我不介意将其限制为 C# 和 VB.NET for .NET Framework 3.0 及更高版本。 (说实话,我无法想象此时要回到 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# 语言中的委托(delegate)长期以来一直具有协变和逆变,以至于感觉“内在”,但事实并非如此。它甚至可能是在 CLR 中启用的功能,我不知道,但是直到 .NET Framework 3.0 (VB.NET 2008) 之前,Visual Basic .NET 才为其委托(delegate)获得协变和逆变功能。因此,用于 .NET 2.0 及以下版本的 Visual Basic.NET 将无法使用这种方法。

比如上面的例子可以翻译成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 上测试了它,只是为了确定,它不能编译,说明:

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)'



基本上,委托(delegate)在 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,其中指出:

"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."



当然,我们知道这一切,无论如何都在违反规则。 (在任何情况下,如果愿意,所有事件处理程序都可以在其签名中使用标准的“对象发送者”——这是一个非破坏性的更改。)

于是使用了SuppressMessageAttribute诀窍:
[SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly",
    Justification = "Using strong-typed GenericEventHandler<TSender, TEventArgs> event handler pattern.")]

我希望这种方法在 future 的某个时候成为标准。它真的很好用。

谢谢大家的意见,我真的很感激......

迈克

最佳答案

微软似乎已经注意到了这一点,因为现在 MSDN 上有一个类似的例子:

Generic Delegates

关于c# - .NET 中的事件签名 - 使用强类型 'Sender' 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1046016/

相关文章:

c# - Unity - 让对象消失一段时间然后再次显示

.net - WCF - Binding.ReceiveTimeout & ReliableSession.InactivityTimeout

c# - 从通用列表 <Datarow> 转换为通用列表 <string> 时出错

mysql - 如何在同一行的datagridview中插入2个不同的数组字符串

c# - 在 Windows Phone 8 中更改 UriMapper 后重定向页面

c# - 采用 C# sha512 哈希并在 php 中进行比较

c# - 无法在 .Net 中预览多页 tiff 文件

c# - 使用 StackExchange.Redis 的循环发布/订阅

sql-server - 无法加载文件或程序集 'System.Data.SqlServerCE'

vb.net - 如何重写这个涉及检查相等运算符重载中的三个 Nullable(Of T) 值的条件逻辑?