【问题标题】:How do static events compare to non-static events in C#?静态事件与 C# 中的非静态事件相比如何?
【发布时间】:2011-08-12 19:52:46
【问题描述】:

我刚刚意识到存在静态事件 - 我很好奇人们如何使用它们。我想知道相对比较如何支持静态与实例方法。例如,静态方法基本上是一个全局函数。但我总是将事件与对象的实例相关联,而我在全局层面考虑它们时遇到了麻烦。

这里有一些代码可以参考,如果它有助于解释:

void Main()
{
    var c1 = new C1();
    c1.E1 += () => Console.WriteLine ("E1");
    C1.E2 += () => Console.WriteLine ("E2");
    c1.F1();
}

// <<delegate>>+D()
public delegate void D();

// +<<event>>E1
// +<<class>><<event>>E2
// +F()
//      <<does>>
//          <<fire>>E1
//          <<fire>>E2
public class C1
{
    public void F1()
    {
        OnE1();
        OnE2();
    }
    public event D E1;
    private void OnE1()
    {
        if(E1 != null)
        {
            E1();
        }
    }
    static public event D E2;
    static private void OnE2()
    {
        if(E2 != null)
        {
            E2();
        }
    }
}

【问题讨论】:

    标签: c# events static-members


    【解决方案1】:

    警惕静态事件。请记住,当对象订阅事件时,对该对象的引用由事件的发布者持有。这意味着您必须非常小心地明确取消订阅静态事件,因为它们将使订阅者永远保持活动状态,即您最终可能会遇到内存泄漏的托管等价物。

    【讨论】:

    • 好点。然而,在某些情况下,这可能不是问题。有时处理程序也可以是静态的。或者它可能生活在一个单身人士身上。
    • @TheDag:当然,或者有时订阅的对象在应用程序的整个生命周期中都存在。问题是您现在每次订阅此静态事件时都必须考虑。它增加了精神开销,并不是每个人都会意识到发生了什么。
    • 公平地说,尚不清楚为什么这似乎出乎很多人的意料。这实际上与如果您有一个引用对象的静态变量或属性没有任何不同,那么该对象将通过该引用保持活动状态,直到删除该引用。在这方面没有什么特别的事件吗?
    【解决方案2】:

    很多 OOP 都可以从消息传递的角度来考虑。

    方法调用是从调用者到被调用者的消息(携带参数)和返回值的消息。

    事件是从源到订阅者的消息。因此可能涉及两个实例,一个发送消息,一个接收消息。

    对于静态事件,没有发送实例(只是一个类型,可能是也可能不是类)。仍然可以将接收者实例编码为委托的目标。

    【讨论】:

      【解决方案3】:

      如果您不熟悉静态方法

      您可能已经熟悉静态方法。如果你不是,一个易于理解的区别是你不需要创建对象的实例来使用静态方法,但你需要创建对象的实例来调用非静态方法。

      一个很好的例子是 System.IO.Directory 和 System.IO.DirectoryInfo 类。

      Directory 类提供静态方法,而 DirectoryInfo 类不提供。

      这里有两篇文章对它们进行了描述,让你自己看看区别。

      http://visualcsharptutorials.com/2011/01/system-io-directory-class/

      http://visualcsharptutorials.com/2011/01/system-io-directoryinfo-class/

      现在开始静态事件...

      但是,在野外很少看到静态事件。在极少数情况下,我认为 opf 是我真正想要使用的,但有一篇 CodeProject 文章确实显示了一种潜在用途。

      http://www.codeproject.com/KB/cs/staticevent.aspx

      这里的关键思想来自解释(我添加的粗体以指出相关文本):

      我们将此属性视为一个单独的对象,并确保存在 一次只有一个实例。和所有实例 交易知道在需要时在哪里可以找到它。有罚款 虽然不同。交易不需要知道 汇率发生变化,而不是他们将使用最后一个 通过请求当前的值在他们使用它时更改了值 价值。 这还不够,例如,我们想要实现一个 用户界面对更改立即做出反应的应用程序 像字体这样的 UI 特征,好像它必须发生在 实时。如果我们可以有一个静态属性会很容易 在 Font 类中调用 currentFont 和一个静态方法来改变 该值和所有实例的静态事件,让他们知道何时 他们需要更新他们的外表。

      作为 .NET 开发人员,我们接受过使用断开连接模型的培训。将 ADO.NET 与经典 ADO 相比较。在 VB6 应用程序中,您可以使用允许以下功能的数据控件:如果您在 PC 上运行该应用程序,则当另一台 PC 上的某人编辑数据时,您的网格中的数据会更新。

      这不是 .NET 开发人员习惯的。我们非常习惯于断开连接的模型。 静态事件可实现更加“互联”的体验。 (即使这种体验我们已经不习惯了。)

      【讨论】:

        【解决方案4】:
        【解决方案5】:

        静态成员不是“全局的”,它们只是的成员,而不是类实例的成员。对于事件和方法、属性、字段等都是如此。

        我无法给出使用静态事件的示例,因为我通常认为静态成员在大多数情况下没有用处。 (他们倾向于暗示反模式,比如 Singleton。)

        【讨论】:

        • 所以订阅者列表也属于班级级别,对吗?这是否意味着多个线程同时订阅会导致坏事?
        • 静态事件的默认实现将使用Interlocked.CompareExchange,这意味着在比赛的情况下,其中一个线程将不会被订阅。
        • @cdhowie:Interlocked.CompareExchange 的用法非常愚蠢。通常比较和交换与重试循环配对。
        • Console.CancelKeyPress 是静态事件的一个很好的例子。控制台可能是单例或静态类,它是后者,因此可以选择不提供任何事件或提供静态事件。后者。 :)
        • @TheDag 在基本层面上,这似乎是有道理的。但是,我仍然更喜欢使用接口,这会排除静态成员的暴露。 (例如,.NET 框架使得无法为单元测试制作模拟控制台实现。)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-18
        • 1970-01-01
        • 2011-01-17
        相关资源
        最近更新 更多