簡體   English   中英

為什么WeakEventManager在發件人不是名義上時不會觸發事件?

[英]Why WeakEventManager does not fire an event when the sender is not the nominal?

我不喜歡不合標准的模式,但我正在對我的應用程序進行快速測試,並且我遇到了這種奇怪的行為。

考慮一個暴露事件的普通類,這里是非常常見的PropertyChanged,但我認為可能是其他任何一個。

訂戶選擇通過WeakEventManager幫助程序訂閱事件。 現在,“奇怪”的東西是實際的發送者引用:只要實例與訂閱上使用的實例相同,一切都很順利。 但是,當您使用其他對象時,不會發出通知。

同樣,這不是一個好的模式,但我想知道這個限制是否有任何好的理由,或者說這是一種錯誤。 更多的是好奇心而不是真正的需求。

class Class1
{
    static void Main(string[] args)
    {
        var c = new MyClass();

        WeakEventManager<INotifyPropertyChanged, PropertyChangedEventArgs>.AddHandler(
            c,
            "PropertyChanged",
            Handler
            );

        c.ActualSender = c;
        c.Number = 123;  //will raise

        c.ActualSender = new Class1();
        c.Number = 456;  //won't raise

        Console.ReadKey();
    }

    static void Handler(object sender, PropertyChangedEventArgs e)
    {
        Console.WriteLine("Handled!");
    }
}

class MyClass : INotifyPropertyChanged
{
    public object ActualSender { get; set; }


    private int _number;
    public int Number
    {
        get { return this._number; }
        set
        {
            if (this._number != value)
            {
                this._number = value;
                this.OnPropertyChanged("Number");
            }
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(
        string name
        )
    {
        this.PropertyChanged(
            this.ActualSender, 
            new PropertyChangedEventArgs(name)
            );
    }
}

編輯:這是一種實現預期行為的粗略方法(為簡單起見,硬鏈接)。

class Class1
{
    static void Main(string[] args)
    {
        var cx = new MyClass();
        var cy = new MyClass();

        Manager.AddHandler(cx, Handler1);
        Manager.AddHandler(cx, Handler2);
        Manager.AddHandler(cy, Handler1);
        Manager.AddHandler(cy, Handler2);

        cx.ActualSender = cx;
        cx.Number = 123;

        cx.ActualSender = new Class1();
        cx.Number = 456;

        cy.ActualSender = cy;
        cy.Number = 789;

        cy.ActualSender = new Class1();
        cy.Number = 555;

        Console.ReadKey();
    }

    static void Handler1(object sender, PropertyChangedEventArgs e)
    {
        var sb = new StringBuilder();
        sb.AppendFormat("Handled1: {0}", sender);

        var c = sender as MyClass;
        if (c != null) sb.AppendFormat("; N={0}", c.Number);
        Console.WriteLine(sb.ToString());
    }

    static void Handler2(object sender, PropertyChangedEventArgs e)
    {
        var sb = new StringBuilder();
        sb.AppendFormat("Handled2: {0}", sender);

        var c = sender as MyClass;
        if (c != null) sb.AppendFormat("; N={0}", c.Number);
        Console.WriteLine(sb.ToString());
    }
}

static class Manager
{
    private static Dictionary<object, Proxy> _table = new Dictionary<object, Proxy>();

    public static void AddHandler(
        INotifyPropertyChanged source,
        PropertyChangedEventHandler handler
        )
    {
        var p = new Proxy();
        p._publicHandler = handler;
        source.PropertyChanged += p.InternalHandler;
        _table[source] = p;
    }

    class Proxy
    {
        public PropertyChangedEventHandler _publicHandler;
        public void InternalHandler(object sender, PropertyChangedEventArgs args)
        {
            this._publicHandler(sender, args);
        }
    }
}

我沒有找到任何有關此內容的文檔,但您可以查看WeakEventManager源代碼以了解發生這種情況的原因。

管理器保持一個表,將已注冊的源對象映射到其處理程序。 請注意,此源對象是您在添加處理程序時傳遞的對象。

當管理器收到事件時,它會使用事件的發送者作為密鑰從該表中查找相關的處理程序。 顯然,如果此發件人與注冊的發件人不同,則找不到預期的處理程序。


編輯

下面是一些偽代碼來說明。

public class PseudoEventManager : IWeakEventListener
{
    private static PseudoEventManager _instance = new PseudoEventManager();

    private readonly Dictionary<object, List<object>> _handlerTable 
                             = new Dictionary<object, List<object>>();

    public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
    {
        foreach (var handler in _handlerTable[sender]) // point of interest A
            //invoke handler
    }

    public static void AddHandler(object source, object handler)
    {
        if (!_instance._handlerTable.ContainsKey(source)) 
            _instance._handlerTable.Add(source, new List<object>()); //point of interest B
        _instance._handlerTable[source].Add(handler);
        //attach to event
    }
}

添加處理程序時,傳入的源將添加到查找表中。 收到事件后,將為此事件的發件人查詢此表,以獲取此發件人/來源的相關處理程序。

在您的示例中,您正在偵聽的源是c ,這是第一次也是ActualSender的值。 因此,事件的發送者與注冊的源相同,這意味着正確地找到並調用了處理程序。

但是,第二次, ActualSender是一個與c不同的實例。 注冊的源不會更改,但sender參數的值現在不同了! 因此,它將無法檢索處理程序,也無法調用任何內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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