2015-06-15 113 views
3

我重构了一些旧代码,在那里我有很多的静态事件的这样辛格尔顿事件

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

我发现使用lazy singletons东西往往比使用静态类更好:

  • 没有内存消耗,直到第一次调用Instance;
  • 私有序列化/反序列化。

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

这样的事件清楚地得罪CS1009,但是我有些疑惑:

  • 静态事件sender不有道理,为什么和在什么情况下会独居sender有什么用处?我只能考虑反序列化,但它是一个内部类实现(此外,类是封闭的),所以我可以小心不要使用事件,如果我需要它,我可以创建私人事件。

  • 创建e(从EventArgs派生)是不必要的复杂到简单地传递参数,我最讨厌的最重要的部分是将其移动到命名空间中的水平,这EventArgs增加的唯一的事(可能有时是有用的)是Empty,然后你有几十个课...EventArgs。我可以想到有时你需要CancelHandled机制,但它对我来说是永远不需要的。

使用事件时,大家都期待(object sender, SomeEventArgs args),这是唯一的原因吗?总结一下,下面是我的主要问题(但是我希望澄清其他问题):CS1009和singletons,我应该修复事件还是简单地禁止消息?

P.S .:相关主题:this,thisthis


我发现this的问题。根据event design guidelines我不得不使用Event<T>(无视this问题),其中T是基于EventArgs类。

关于静态事件sender

静态事件,则sender参数应为空。

这是一个设计指导,这可能不是小艾,但会被其他人非常欢迎(谁在读/保持我的代码)。

它打破双方KISSYAGNI原则,对我来说。我越想越多,我越来越不确定要做什么。

+0

这取决于项目。如果它是一个巨大的ddd项目,我会尽可能地保留这些(发送者和事件参数),即使它们没有被使用(以保留体系结构)。但如果它只是一个简单的控制台应用程序,它是不同的故事 – Fabjan

回答

2

我会解决你的错误。总体设计指南的确是(object sender, EventArgs e)签名。

这是一个惯例,只关心代码的一致性,代码的可读性...等。遵循这种模式将有助于其他人将处理程序附加到您的事件中。

的一般提示/答案:

  • 对于静态事件,你确实应当使用nullsender(因为根据定义没有发件人的实例)。
  • 如果您没有什么可以通过e参数,请使用EventArgs.Empty而不是new EventArgs()null
  • 你可以使用EventHandlerEventHandler<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>,如果你需要传递两个值。最终,EventArgs<Tuple<>>也将是可能的,但对于大于2点的值我会建立一个特定XXXEventArgs类,而不是因为它更容易阅读XXXEventArgs.MyNamedBusinessPropertyEventArgs<T1, T2, T3>.Value2EventArgs<Tuple<int, string, bool>>.Value.Item1

关于KISS/YAGNI:请记住(object sender, EventArgs e)约定关于代码一致性。如果某些开发人员使用你的代码的处理程序连接到您的事件之一,我可以向你保证,他将只是爱的事实,你的事件定义为就像在BCL本身任何其他事件的定义,所以他立即知道如何正确使用你的代码。

甚至有有其他的优势不仅仅是代码的一致性/可读性:

我继承了我的自定义从EventArgsXXXEventArgs类,但你可以建立一些基地EventArgs类,并从它继承。例如,请参见MouseEventArgs以及从其继承的所有类。重复使用现有的类比提供具有5/6个相同属性的多个委托签名要好得多。例如:

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属性添加到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); 
+0

'EventHandler >'很酷,谢谢。但是对于多个参数,它会非常讨厌'Tuple <>'对吗?你对KISS和YAGNI有意见吗?两者都说我不应该对我的'Updated'事件做任何建议(没有发送者 - 因为'static'或singleton,没有参数)。你看,我正在寻找一种*最终*争论来说服我,这是一个无与伦比的优势。 – Sinatr

+0

@Sinatr我编辑答案提供更多信息 – ken2k

+0

我觉得很确定。感谢所有的努力! – Sinatr