单身人士事件

时间:2015-06-15 10:32:59

标签: c# events

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

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

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

  • 在第一次Instance呼叫之前没有消耗内存;
  • 私有序列化/反序列化。

因此我将这些重构为单身者,现在我有代码分析抱怨。

这些事件明显冒犯CS1009,但我有些怀疑:

  • 对于静态事件sender没有意义,为什么以及在什么情况下单例sender会有用?我只能考虑反序列化,但它是一个内部类实现(而且类是密封的),所以我可以注意不使用事件,如果我需要事件,我可以创建私有事件。

    < / LI>
  • 创建e(从EventArgs派生)是简单传递参数的不必要的复杂因素,我讨厌的最大部分是将其移动到命名空间级别,{{1} }添加(有时可能有用)是EventArgs然后你有几十个类Empty。我可以想到有时你需要CancelHandled机制,但我从未需要它。

当使用事件时,每个人都期望...EventArgs,这是唯一的原因吗?

总结一下,这是我的问题(但我希望澄清其他问题):CS1009和单身,我应该修复事件还是简单地压制消息?

P.S。:相关主题:thisthisthis

我发现了this个问题。根据{{​​3}},我必须使用(object sender, SomeEventArgs args)(无视event design guidelines个问题),其中Event<T>基于T类。

关于静态事件中的EventArgs

  

在静态事件上,sender参数应为null。

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

它打破了thisKISS原则。我越是想到它,我越来越不确定该怎么做。

1 个答案:

答案 0 :(得分:2)

我会修复你的错误。一般设计准则确实是>>> n = 'James Earl Carter, Jr.'.split(',')[0].split(' ')[-1] >>> print(n) Carter >>> m = 'Ronald Wilson Reagan'.split(',')[0].split(' ')[-1] >>> print(m) Reagan 签名。

这是一个约定,是关于代码一致性,代码可读性等等。遵循此模式将有助于其他人将处理程序附加到您的活动中。

一些一般提示/答案:

  • 对于静态事件,您确实应将(object sender, EventArgs e)用作null(因为每个定义没有发件人实例)。
  • 如果您没有为sender参数传递任何内容,请使用e代替EventArgs.Emptynew EventArgs()
  • 您可以使用nullEventHandler来简化活动的定义。
  • 为简单起见,如果要将单个值传递给事件处理程序,可以使用从EventHandler<T>继承的自定义EventArgs<T>类。

如果你想使用带有EventArgs定义的单例模式,这里有一个完整的例子。请注意,事件不是Lazy<T>,因此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(); } } 。最终,EventArgs<T1, T2>也是可能的,但是对于超过2个值,我会建立一个特定的EventArgs<Tuple<>>类,因为它比XXXEventArgs或{{1更容易阅读XXXEventArgs.MyNamedBusinessProperty }}

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

除了代码一致性/可读性之外,还有其他优势:

我从EventArgs<Tuple<int, string, bool>>.Value.Item1继承了我的自定义(object sender, EventArgs e)类,但您可以构建一些基类XXXEventArgs类并从中继承。例如,请参阅MouseEventArgs以及从中继承的所有类。重用现有类比提供具有5/6相同属性的多个委托签名要好得多。例如:

EventArgs

另一点是,使用EventArgs可以简化代码可维护性。想象一下以下场景:

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添加一些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