我会修正你的错误。一般的设计准则确实是(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<Tuple<>> 也是可能的,但对于超过 2 个值,我将构建一个特定的 XXXEventArgs 类,因为它比 EventArgs<T1, T2, T3>.Value2 或 EventArgs<Tuple<int, string, bool>>.Value.Item1 更容易阅读 XXXEventArgs.MyNamedBusinessProperty。
关于 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);