单身事件 [英] Singleton events

查看:171
本文介绍了单身事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在重构一些旧代码,其中有很多这样的静态事件

I am refactoring some old code, where I have a lot of static events like this

public static event Action Updated;
public static void OnUpdated()
{
    if (Updated != null)
        Updated();
}

我发现使用 lazy singletons 通常比使用静态类更好:

I found out what using lazy singletons is often better than using static classes:


  • 直到第一个 Instance call;

  • 私有序列化/反序列化时,不会消耗内存。

  • no memory is consumed until first Instance call;
  • private serialization/deserialization.

所以我重构那些单身,现在我有代码分析的抱怨。

So that I refactor those to singletons and now I have Code Analysis complains.

这样的事件显然是冒犯 CS1009 ,但我有一些疑问:

Such events clearly offend CS1009 but I have some doubts:


  • 对于静态事件发件人没有意义,为什么和在什么情况下singleton 发件人有什么用吗?我只能想一下反序列化,但它是一个内部的类实现(此外,类被封装),所以我可以小心不要使用事件,如果我需要一个,我可以创建私人事件。

  • for static events sender doesn't makes sense, why and in what scenario would singleton sender be of any use? I can only think about deserialization, but it's an internal class implementation (moreover class is sealed), so I can take care to not use events and if I need one, I can create private event.

创建 e (派生自 EventArgs )是简单传递参数的不必要的复杂性,我讨厌的最大的部分是将其移动到命名空间级别, EventArgs 添加(有时可能有用)的唯一的事情是然后你有几十个类 ... EventArgs 。我可以想想你有什么需要取消或者处理的机制,但它是neverd

creating e (deriving from EventArgs) is unnecessary complication to simply pass parameters, the biggest part I hate is to move it into namespace level, the only thing which EventArgs adds (could be useful sometimes) is Empty and then you have dozens of classes ...EventArgs. I could think what sometimes you need Cancel or Handled mechanics, but it was neverd needed for me.

使用事件时,每个人都希望(object sender,SomeEventArgs args) 这是唯一的原因吗?

When using event does everybody expect (object sender, SomeEventArgs args) and this is the only reason?

总而言之,这里是我的主要问题(但我希望澄清其他问题):CS1009和单身,我应该修复事件还是简单地抑制消息?

To summarize, here is my main question (but I would appreciate clarifying other questions as well): CS1009 and singletons, should I fix events or simply suppress the message?

PS:相关主题:这个

P.S.: related subjects: this, this and this.

我发现这个问题。根据事件设计指南,我必须使用事件< T> (忽略问题),其中 T 基于 EventArgs 类。

I found this question. According to event design guidelines I have to use Event<T> (disregards to this question), where T is based on EventArgs class.

关于发件人在静态事件中:


在静态事件中,发件人参数应为null。

这是一个设计指南,可能看起来不漂亮对我,但是会受到其他人(正在阅读/维护我的代码)的欢迎。

This is a design guideline, which may not looks pretty to me, but will be very welcomed by anybody else (who is reading/maintaining my code).

它打破了 KISS YAGNI 原则对我来说。我越想想,越来越少我确定要做什么。

It breaks both KISS and YAGNI principles as for me. And the more I think about it, the less and less I am sure of what to do.

推荐答案

我会修复你的错误。一般设计指南确实是(对象发件人,EventArgs e)签名。

I would fix your error. The general design guideline is indeed the (object sender, EventArgs e) signature.

这是一个约定,都是关于代码一致性,代码可读性等等。

It's a convention and is all about code consistency, code readability...etc. Following this pattern will help other people attaching handlers to your events.

一些一般的提示/答案:

Some general tips/answers:


  • 对于静态事件,您应该使用 null 作为发件人(因为没有发件人

  • 如果您没有通过 e 参数,请使用 EventArgs.Empty 而不是新的EventArgs() null / li>
  • 您可以使用 EventHandler EventHandler< T> 来简化您的活动。

  • 为了简单起见,您可以使用继承自 EventArgs< T> > EventArgs 如果你想传递一个值到你的事件处理程序。

  • For a static event, you should indeed use null as sender (because there is no sender instance per definition).
  • If you have nothing to pass for the e parameter, use EventArgs.Empty instead of new EventArgs() or null.
  • You could use EventHandler and EventHandler<T> to simplify the definition of your events.
  • For the sake of simplicity, you could use a custom EventArgs<T> class inheriting from EventArgs if you want to pass a single value to your event handler.

如果你想使用具有 Lazy< T> 定义的单例模式,这里是一个完整的例子。做注意事项不是 static ,所以 sender 参数包含对单例实例的引用:

If you want to use the singleton pattern with a Lazy<T> definition, here's a complete example. Do note no event is static, so the sender parameter contains a reference to the singleton instance:

public class EventArgs<T> : EventArgs
{
    public EventArgs(T value)
    {
        this.Value = value;
    }

    public T Value { get; set; }
}

public class EventArgs2 : EventArgs
{
    public int Value { get; set; }
}

internal static class Program
{
    private static void Main(string[] args)
    {
        Singleton.Instance.MyEvent += (sender, e) => Console.WriteLine("MyEvent with empty parameter");
        Singleton.Instance.MyEvent2 += (sender, e) => Console.WriteLine("MyEvent2 with parameter {0}", e.Value);
        Singleton.Instance.MyEvent3 += (sender, e) => Console.WriteLine("MyEvent3 with parameter {0}", e.Value);

        Singleton.Instance.Call();

        Console.Read();
    }
}

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance { get { return lazy.Value; } }

    /// <summary>
    /// Prevents a default instance of the <see cref="Singleton"/> class from being created.
    /// </summary>
    private Singleton()
    {
    }

    /// <summary>
    /// Event without any associated data
    /// </summary>
    public event EventHandler MyEvent;

    /// <summary>
    /// Event with a specific class as associated data
    /// </summary>
    public event EventHandler<EventArgs2> MyEvent2;

    /// <summary>
    /// Event with a generic class as associated data
    /// </summary>
    public event EventHandler<EventArgs<int>> MyEvent3;

    public void Call()
    {
        if (this.MyEvent != null)
        {
            this.MyEvent(this, EventArgs.Empty);
        }

        if (this.MyEvent2 != null)
        {
            this.MyEvent2(this, new EventArgs2 { Value = 12 });
        }

        if (this.MyEvent3 != null)
        {
            this.MyEvent3(this, new EventArgs<int>(12));
        }

        Console.Read();
    }
}






编辑:


如果需要传递两个值,您还可以构建一些 EventArgs< T1,T2> 最终, EventArgs< Tuple<>> 也是可能的,但是对于超过2个值,我将构建一个特定的 XXXEventArgs class,因为它更容易阅读 XXXEventArgs.MyNamedBusinessProperty EventArgs< T1,T2,T3> .Value2 EventArgs< Tuple< int,string,bool>> .Value.Item1

You could also build some EventArgs<T1, T2> if you need to pass two values. Ultimately, EventArgs<Tuple<>> would also be possible, but for more than 2 values I would build a specific XXXEventArgs class instead as it's easier to read XXXEventArgs.MyNamedBusinessProperty than EventArgs<T1, T2, T3>.Value2 or EventArgs<Tuple<int, string, bool>>.Value.Item1.

关于KISS / YAGNI :记住(对象发件人,EventArgs e)约定是关于代码一致性。如果一些开发人员使用您的代码将处理程序附加到您的一个事件中,我可以向您保证,他将只是喜欢您的事件定义就像BCL中的任何其他事件定义一样本身,所以他立即知道如何正确使用你的代码。

Regarding KISS/YAGNI: remember the (object sender, EventArgs e) convention is all about code consistency. If some developer uses your code to attach an handler to one of your events, I can assure you he will just love the fact that your event definition is just like any other event definition in the BCL itself, so he instantly knows how to properly use your code.

甚至有其他优点,而不仅仅是代码一致性/可读性:

There even are others advantages than just code consistency/readability:

我从 EventArgs 继承了我的自定义 XXXEventArgs 类,但是你可以建立一些基础 EventArgs 类并继承它。例如,请参阅 MouseEventArgs 以及从它继承的所有类。更好地重用现有类,而不是提供具有5/6相同属性的多个委托签名。例如:

I inherited my custom XXXEventArgs classes from EventArgs, but you could build some base EventArgs class and inherit from it. For instance, see MouseEventArgs and all classes inheriting from it. Much better to reuse existing classes than to provide multiple delegate signatures with 5/6 identical properties. For instance:

public class MouseEventArgs : EventArgs
{
    public int X { get; set; }
    public int Y { get; set; }
}

public class MouseClickEventArgs : MouseEventArgs
{
    public int ButtonType { get; set; }
}

public class MouseDoubleClickEventArgs : MouseClickEventArgs
{
    public int TimeBetweenClicks { get; set; }
}

public class Test
{
    public event EventHandler<MouseClickEventArgs> ClickEvent;
    public event EventHandler<MouseDoubleClickEventArgs> DoubleClickEvent;
}

public class Test2
{
    public delegate void ClickEventHandler(int X, int Y, int ButtonType);
    public event ClickEventHandler ClickEvent;

    // See duplicated properties below =>
    public delegate void DoubleClickEventHandler(int X, int Y, int ButtonType, int TimeBetweenClicks);
    public event DoubleClickEventHandler DoubleClickEvent;
}

另一点是,使用 EventArgs 可以简化代码的可维护性。想象下面的情况:

Another point is, using EventArgs could simplify code maintainability. Imagine the following scenario:

public MyEventArgs : EventArgs
{
    public string MyProperty { get; set; }
}
public event EventHandler<MyEventArgs> MyEvent;
...
if (this.MyEvent != null)
{
    this.MyEvent(this, new MyEventArgs { MyProperty = "foo" });
}
...
someInstance.MyEvent += (sender, e) => SomeMethod(e.MyProperty);

如果您想添加一些 MyProperty2 属性到 MyEventArgs ,您可以不修改所有现有的事件监听器:

In case you want to add some MyProperty2 property to MyEventArgs, you could do it without modifying all your existing event listeners:

public MyEventArgs : EventArgs
{
    public string MyProperty { get; set; }
    public string MyProperty2 { get; set; }
}

public event EventHandler<MyEventArgs> MyEvent;
...
if (this.MyEvent != null)
{
    this.MyEvent(this, new MyEventArgs { MyProperty = "foo", MyProperty2 = "bar" });
}
...
// I didn't change the event handler. If SomeMethod() doesn't need MyProperty2, everything is just fine already
someInstance.MyEvent += (sender, e) => SomeMethod(e.MyProperty);

这篇关于单身事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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