gpt4 book ai didi

c# - 单例事件

转载 作者:行者123 更新时间:2023-11-30 16:54:05 26 4
gpt4 key购买 nike

我正在重构一些旧代码,其中有很多这样的静态事件

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

我发现了使用 lazy singletons 的原因通常比使用静态类更好:

  • 直到第一个 Instance 才消耗内存打电话;
  • 私有(private)序列化/反序列化。

所以我将它们重构为单例,现在我收到了代码分析的提示。

此类事件显然冒犯了CS1009但我有一些疑问:

  • 用于静态事件 sender没有意义,为什么以及在什么情况下会单例 sender有什么用我只能考虑反序列化,但它是一个内部类实现(而且类是密封的),所以我可以注意不要使用事件,如果需要,我可以创建私有(private)事件。

  • 创建 e (源自 EventArgs )对于简单地传递参数来说是不必要的复杂化,我最讨厌的部分是将它移到 namespace 级别,这是 EventArgs 唯一需要做的事情添加(有时可能有用)是 Empty然后你有几十个类...EventArgs .我能想到有时你需要什么CancelHandled机械,但我从来不需要它。

大家在使用事件的时候期待(object sender, SomeEventArgs args)这是唯一的原因吗?

总而言之,这是我的主要问题(但我也很乐意澄清其他问题):CS1009 和单例,我应该修复事件还是简单地抑制消息?

P.S.:相关科目:this , thisthis .


我找到了 this问题。根据event design guidelines我必须使用 Event<T> (忽略 this 问题),其中 T基于EventArgs类。

关于 sender在静态事件中:

On static events, the sender parameter should be null.

这是一个设计指南对我来说可能不太好看,但会受到其他人(正在阅读/维护我的代码的人)的欢迎。

它打破了KISSYAGNI原则对我来说。而且我想得越多,我就越不确定该怎么做。

最佳答案

我会修正你的错误。一般设计指南确实是 (object sender, EventArgs e)签名。

这是一个约定,与代码一致性、代码可读性等有关。遵循此模式将帮助其他人将处理程序附加到您的事件。

一些一般提示/答案:

  • 对于静态事件,您确实应该使用 null作为sender (因为没有发送者实例按照定义)。
  • 如果您对 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 class 而不是因为它更容易阅读 XXXEventArgs.MyNamedBusinessPropertyEventArgs<T1, T2, T3>.Value2EventArgs<Tuple<int, string, bool>>.Value.Item1 .

关于 KISS/YAGNI:记住 (object sender, EventArgs e)约定就是代码一致性。如果某些开发人员使用您的代码将处理程序附加到您的事件之一,我可以向您保证,他会非常喜欢您的事件定义就像 BCL 中的任何其他事件定义一样本身,因此他立即知道如何正确使用您的代码。

除了代码一致性/可读性之外,还有其他优势:

我继承了我的习惯XXXEventArgs来自 EventArgs 的类(class), 但你可以建立一些基地 EventArgs类并从中继承。例如,参见 MouseEventArgs 以及继承自它的所有类。重用现有类比提供具有 5/6 相同属性的多个委托(delegate)签名要好得多。例如:

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属性(property)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);

关于c# - 单例事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30842956/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com