单身事件 [英] 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();
}
我发现使用 lazy singletons 通常比使用静态类更好:
I found out what using lazy singletons is often better than using static classes:
- 直到第一个
Instance
call; - 私有序列化/反序列化时,不会消耗内存。
- no memory is consumed until first
Instance
call; - private serialization/deserialization.
所以我重构那些单身,现在我有代码分析的抱怨。
So that I refactor those to singletons and now I have Code Analysis complains.
这样的事件显然是冒犯 CS1009 ,但我有一些疑问:
Such events clearly offend CS1009 but I have some doubts:
-
对于静态事件
发件人
没有意义,为什么和在什么情况下singleton发件人
有什么用吗?我只能想一下反序列化,但它是一个内部的类实现(此外,类被封装),所以我可以小心不要使用事件,如果我需要一个,我可以创建私人事件。
for static events
sender
doesn't makes sense, why and in what scenario would singletonsender
be of any use? 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.
创建 e
(派生自 EventArgs
)是简单传递参数的不必要的复杂性,我讨厌的最大的部分是将其移动到命名空间级别, EventArgs
添加(有时可能有用)的唯一的事情是空
然后你有几十个类 ... EventArgs
。我可以想想你有什么需要取消或者处理的机制,但它是neverd
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
. I could think what sometimes you need Cancel or Handled mechanics, but it was neverd needed for me.
使用事件时,每个人都希望(object sender,SomeEventArgs args)
这是唯一的原因吗?
When using event does everybody expect (object sender, SomeEventArgs args)
and this is the only reason?
总而言之,这里是我的主要问题(但我希望澄清其他问题):CS1009和单身,我应该修复事件还是简单地抑制消息?
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?
P.S.: related subjects: this, this and this.
我发现这个问题。根据事件设计指南,我必须使用事件< T>
(忽略此问题),其中 T
基于 EventArgs
类。
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.
关于发件人
在静态事件中:
在静态事件中,
发件人
参数应为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).
它打破了 KISS 和 YAGNI 原则对我来说。我越想想,越来越少我确定要做什么。
It breaks both KISS and YAGNI principles as for me. And the more I think about it, the less and less I am sure of what to do.
推荐答案
我会修复你的错误。一般设计指南确实是(对象发件人,EventArgs e)
签名。
I would fix your error. The general design guideline is indeed the (object sender, EventArgs e)
signature.
这是一个约定,都是关于代码一致性,代码可读性等等。
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
作为发件人
(因为没有发件人 - 如果您没有通过
e
参数,请使用EventArgs.Empty
而不是新的EventArgs()
或null
/ li>
- 您可以使用
EventHandler
和EventHandler< T>
来简化您的活动。 - 为了简单起见,您可以使用继承自
EventArgs< T>
> EventArgs 如果你想传递一个值到你的事件处理程序。
- For a static event, you should indeed use
null
assender
(because there is no sender instance per definition). - If you have nothing to pass for the
e
parameter, useEventArgs.Empty
instead ofnew EventArgs()
ornull
. - You could use
EventHandler
andEventHandler<T>
to simplify the definition of your events. - For the sake of simplicity, you could use a custom
EventArgs<T>
class inheriting fromEventArgs
if you want to pass a single value to your event handler.
如果你想使用具有 Lazy< T>
定义的单例模式,这里是一个完整的例子。做注意事项不是 static
,所以 sender
参数包含对单例实例的引用:
If you want to use the singleton pattern with a Lazy<T>
definition, here's a complete example. Do note no event is static
, so the sender
parameter contains a reference to the singleton instance:
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
class,因为它更容易阅读 XXXEventArgs.MyNamedBusinessProperty
比 EventArgs< T1,T2,T3> .Value2
或 EventArgs< Tuple< int,string,bool>> .Value.Item1
。
You could also build some EventArgs<T1, T2>
if you need to pass two values. 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
.
关于KISS / YAGNI :记住(对象发件人,EventArgs e)
约定是关于代码一致性。如果一些开发人员使用您的代码将处理程序附加到您的一个事件中,我可以向您保证,他将只是喜欢您的事件定义就像BCL中的任何其他事件定义一样本身,所以他立即知道如何正确使用你的代码。
Regarding KISS/YAGNI: remember the (object sender, EventArgs e)
convention is all about code consistency. 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.
甚至有其他优点,而不仅仅是代码一致性/可读性:
There even are others advantages than just code consistency/readability:
我从 EventArgs
继承了我的自定义 XXXEventArgs
类,但是你可以建立一些基础 EventArgs
类并继承它。例如,请参阅 MouseEventArgs
以及从它继承的所有类。更好地重用现有类,而不是提供具有5/6相同属性的多个委托签名。例如:
I inherited my custom XXXEventArgs
classes from EventArgs
, but you could build some base EventArgs
class and inherit from it. For instance, see MouseEventArgs
and all classes inheriting from it. Much better to reuse existing classes than to provide multiple delegate signatures with 5/6 identical properties. 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;
}
另一点是,使用 EventArgs
可以简化代码的可维护性。想象下面的情况:
Another point is, using EventArgs
could simplify code maintainability. 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);
如果您想添加一些 MyProperty2
属性到 MyEventArgs
,您可以不修改所有现有的事件监听器:
In case you want to add some MyProperty2
property to MyEventArgs
, you could do it without modifying all your existing event listeners:
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);
这篇关于单身事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!