簡體   English   中英

單身人士事件

[英]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和單身人士,我應該修復事件還是簡單地抑制消息?

PS:相關科目: 這個這個這個


我發現了這個問題。 根據事件設計指南,我必須使用Event<T> (忽略這個問題),其中T基於EventArgs類。

關於靜態事件中的sender

在靜態事件上, sender參數應為null。

這是一個設計指南對我來說可能看起來不太 ,但是會受到其他人的歡迎(他正在閱讀/維護我的代碼)。

它對我來說打破了KISSYAGNI的原則。 我想的越多,我越不確定該怎么做。

我會解決你的錯誤。 一般設計准則確實是(object sender, EventArgs e)簽名。

這是一個約定,是關於代碼一致性,代碼可讀性等等。 遵循此模式將幫助其他人將處理程序附加到您的事件。

一些一般提示/答案:

  • 對於靜態事件,您確實應該使用null作為sender (因為每個定義沒有發送方實例)。
  • 如果您沒有為e參數傳遞任何內容,請使用EventArgs.Empty而不是new EventArgs()null
  • 您可以使用EventHandlerEventHandler<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>.Value2EventArgs<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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM