[英]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: 我发现使用懒惰单例通常比使用静态类更好:
Instance
call; Instance
调用之前不会消耗内存; 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. 它对我来说打破了KISS和YAGNI的原则。 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: 一些一般提示/答案:
null
as sender
(because there is no sender instance per definition ). null
作为sender
(因为每个定义没有发送方实例)。 e
parameter, use EventArgs.Empty
instead of new EventArgs()
or null
. e
参数传递任何内容,请使用EventArgs.Empty
而不是new EventArgs()
或null
。 EventHandler
and EventHandler<T>
to simplify the definition of your events. EventHandler
和EventHandler<T>
来简化事件的定义。 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>.Value2
或EventArgs<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.