[英]Singleton events
我正在重构一些旧的代码,我有很多像这样的静态事件
public static event Action Updated;
public static void OnUpdated()
{
if (Updated != null)
Updated();
}
我发现使用懒惰单例通常比使用静态类更好:
Instance
调用之前不会消耗内存; 所以我重构那些单身人士,现在我有代码分析抱怨。
这样的事件显然冒犯了CS1009,但我有些疑惑:
对于静态事件, sender
没有意义,为什么以及在什么情况下单例sender
可以使用? 我只能考虑反序列化,但它是一个内部类实现(而且类是密封的),所以我可以注意不使用事件,如果我需要,我可以创建私有事件。
创建e
(从EventArgs
派生)是简单地传递参数的不必要的复杂性,我讨厌的最大部分是将其移动到命名空间级别, EventArgs
添加的唯一东西(有时可能有用)是Empty
,然后你有几十个类...EventArgs
。 我可以想到有时你需要取消或处理机制,但它从来都不需要我。
当使用事件时,每个人都期望(object sender, SomeEventArgs args)
,这是唯一的原因吗?
总而言之,这是我的主要问题(但我希望澄清其他问题):CS1009和单身人士,我应该修复事件还是简单地抑制消息?
我发现了这个问题。 根据事件设计指南,我必须使用Event<T>
(忽略这个问题),其中T
基于EventArgs
类。
关于静态事件中的sender
:
在静态事件上,
sender
参数应为null。
这是一个设计指南 , 对我来说可能看起来不太好 ,但是会受到其他人的欢迎(他正在阅读/维护我的代码)。
我会解决你的错误。 一般设计准则确实是(object sender, EventArgs e)
签名。
这是一个约定,是关于代码一致性,代码可读性等等。 遵循此模式将帮助其他人将处理程序附加到您的事件。
一些一般提示/答案:
null
作为sender
(因为每个定义没有发送方实例)。 e
参数传递任何内容,请使用EventArgs.Empty
而不是new EventArgs()
或null
。 EventHandler
和EventHandler<T>
来简化事件的定义。 EventArgs
继承的自定义EventArgs<T>
类。 如果你想使用带有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<T1, T2>
。 最终, EventArgs<Tuple<>>
也是可能的,但是对于超过2个值,我将构建一个特定的XXXEventArgs
类,因为它比EventArgs<T1, T2, T3>.Value2
或EventArgs<Tuple<int, string, bool>>.Value.Item1
更容易读取XXXEventArgs.MyNamedBusinessProperty
EventArgs<Tuple<int, string, bool>>.Value.Item1
。
关于KISS / YAGNI:记住(object sender, EventArgs e)
约定是关于代码一致性的 。 如果某些开发人员使用您的代码将处理程序附加到您的某个事件中,我可以向您保证,他只会喜欢您的事件定义与BCL本身中的任何其他事件定义一样的事实,因此他立即知道如何正确使用你的代码。
除了代码一致性/可读性之外,还有其他优点:
我从EventArgs
继承了我的自定义XXXEventArgs
类,但你可以构建一些基本的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);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.