简体   繁体   English

单身人士事件

[英]Singleton events

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

I found out what using lazy singletons is often better than using static classes: 我发现使用懒惰单例通常比使用静态类更好:

  • no memory is consumed until first Instance call; 在第一次Instance调用之前不会消耗内存;
  • private serialization/deserialization. 私有序列化/反序列化。

So that I refactor those to singletons and now I have Code Analysis complains. 所以我重构那些单身人士,现在我有代码分析抱怨。

Such events clearly offend CS1009 but I have some doubts: 这样的事件显然冒犯了CS1009,但我有些疑惑:

  • for static events sender doesn't makes sense, why and in what scenario would singleton sender be of any use? 对于静态事件, sender没有意义,为什么以及在什么情况下单例sender可以使用? 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. 我只能考虑反序列化,但它是一个内部类实现(而且类是密封的),所以我可以注意不使用事件,如果我需要,我可以创建私有事件。

  • 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 . 创建e (从EventArgs派生)是简单地传递参数的不必要的复杂性,我讨厌的最大部分是将其移动到命名空间级别, EventArgs添加的唯一东西(有时可能有用)是Empty ,然后你有几十个类...EventArgs I could think what sometimes you need Cancel or Handled mechanics, but it was neverd needed for me. 我可以想到有时你需要取消处理机制,但它从来都不需要我。

When using event does everybody expect (object sender, SomeEventArgs args) and this is the only reason? 当使用事件时,每个人都期望(object sender, SomeEventArgs args) ,这是唯一的原因吗?

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? 总而言之,这是我的主要问题(但我希望澄清其他问题):CS1009和单身人士,我应该修复事件还是简单地抑制消息?

PS: related subjects: this , this and this . PS:相关科目: 这个这个这个


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. 根据事件设计指南,我必须使用Event<T> (忽略这个问题),其中T基于EventArgs类。

Regarding sender in static events: 关于静态事件中的sender

On static events, the sender parameter should be null. 在静态事件上, sender参数应为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). 这是一个设计指南对我来说可能看起来不太 ,但是会受到其他人的欢迎(他正在阅读/维护我的代码)。

It breaks both KISS and YAGNI principles as for me. 它对我来说打破了KISSYAGNI的原则。 And the more I think about it, the less and less I am sure of what to do. 我想的越多,我越不确定该怎么做。

I would fix your error. 我会解决你的错误。 The general design guideline is indeed the (object sender, EventArgs e) signature. 一般设计准则确实是(object sender, EventArgs e)签名。

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: 一些一般提示/答案:

  • For a static event, you should indeed use null as sender (because there is no sender instance per definition ). 对于静态事件,您确实应该使用null作为sender (因为每个定义没有发送方实例)。
  • If you have nothing to pass for the e parameter, use EventArgs.Empty instead of new EventArgs() or null . 如果您没有为e参数传递任何内容,请使用EventArgs.Empty而不是new EventArgs()null
  • You could use EventHandler and EventHandler<T> to simplify the definition of your events. 您可以使用EventHandlerEventHandler<T>来简化事件的定义。
  • 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. 为简单起见,如果要将单个值传递给事件处理程序,可以使用从EventArgs继承的自定义EventArgs<T>类。

If you want to use the singleton pattern with a Lazy<T> definition, here's a complete example. 如果你想使用带有Lazy<T>定义的单例模式,这里有一个完整的例子。 Do note no event is static , so the sender parameter contains a reference to the singleton instance: 请注意,事件不是static ,因此sender参数包含对单例实例的引用:

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

EDIT: 编辑:

You could also build some EventArgs<T1, T2> if you need to pass two values. 如果需要传递两个值EventArgs<T1, T2>也可以构建一些EventArgs<T1, T2> 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 . 最终, EventArgs<Tuple<>>也是可能的,但是对于超过2个值,我将构建一个特定的XXXEventArgs类,因为它比EventArgs<T1, T2, T3>.Value2EventArgs<Tuple<int, string, bool>>.Value.Item1更容易读取XXXEventArgs.MyNamedBusinessProperty EventArgs<Tuple<int, string, bool>>.Value.Item1

Regarding KISS/YAGNI: remember the (object sender, EventArgs e) convention is all about code consistency . 关于KISS / YAGNI:记住(object sender, EventArgs e)约定是关于代码一致性的 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. 如果某些开发人员使用您的代码将处理程序附加到您的某个事件中,我可以向您保证,他只会喜欢您的事件定义与BCL本身中的任何其他事件定义一样的事实,因此他立即知道如何正确使用你的代码。

There even are others advantages than just code consistency/readability: 除了代码一致性/可读性之外,还有其他优点:

I inherited my custom XXXEventArgs classes from EventArgs , but you could build some base EventArgs class and inherit from it. 我从EventArgs继承了我的自定义XXXEventArgs类,但你可以构建一些基本的EventArgs类并从中继承。 For instance, see MouseEventArgs and all classes inheriting from it. 例如,请参阅MouseEventArgs以及从中继承的所有类。 Much better to reuse existing classes than to provide multiple delegate signatures with 5/6 identical properties. 重用现有类比提供具有5/6相同属性的多个委托签名要好得多。 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;
}

Another point is, using EventArgs could simplify code maintainability. 另一点是,使用EventArgs可以简化代码的可维护性。 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);

In case you want to add some MyProperty2 property to MyEventArgs , you could do it without modifying all your existing event listeners: 如果您想将一些MyProperty2属性添加到MyEventArgs ,您可以在不修改所有现有事件侦听器的情况下执行此操作:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM