当属性或变量的值发生变化时触发事件

3
我想为我的项目添加更多功能,该项目使用了打包在.NET Framework中的许多类。这些相同的类提供了许多属性,可以非常有用地调整我的项目的功能,然而,这些类缺少事件。
如果每个属性都有适当的事件,在此类属性更改其值时触发,那么我就可以分配事件处理程序,根据这些属性的值进行操作。
我制作了下面的示例案例,以最简单的方式说明我的目标。
样例案例: System.Net.Sockets.Socket类(Socket on MSDN Docs)有一个名为Connected的属性,基本上返回true,如果套接字连接到指定的端点,则返回false。 我想要实现的很简单。我想保持这个属性“观察”,当它的值发生变化时,触发一个事件。
使用INotifyPropertyChanged接口对我的一个类进行操作会很简单,尽管有点繁琐,因为每当我的代码更改属性值时,我都必须手动触发事件。不幸的是,据我所知,这种过程甚至不能应用于NET Framework中分布的现有Socket类。
好吧,这个问题变得相当广泛了,抱歉,但我希望它能给我的目标提供一些见解。
现在简单地说,我想监视Socket类的Connected属性,当其值发生变化时,触发一个事件。如果还可以使用这种方法来监视变量以及属性,那将是很棒的,不仅适用于我,而且适用于所有在SO上遇到这个问题的人。
当然,最好是采用简单轻量级的方法,但最重要的是,我想了解如何做到这一点,这样将来我就可以将其应用于其他类的大规模操作中。
我意识到我要求很多。非常感谢。

1
我假设您基本上想将某种基于观察者模式的东西附加到现有的不可更改的类(例如套接字示例)上?如果是这样,您会检查值更改吗,比如每个主线程循环?还是使用具有计时器的后台线程? - Chris Sinclair
@ChrisSinclair:那可能是一种方法。我正在寻找任何可用的方法来做到这一点,自己尝试并选择最简单和轻量级的方法。 - Fábio Antunes
我一定是漏掉了什么。我不明白发布的观察者模式如何帮助监视非可修改类/成员(例如Socket.Connected)的任意成员状态变化。 - Chris Sinclair
啊,没事了。我刚才看错了,Rx只是已经被引用的观察者模式(BCL的一部分)吧。 - Chris Sinclair
@ChrisSinclair:不用担心。事实上,我开始觉得.NET类不允许轻松地“观察”属性。这让我想着要从头开始做些什么。 - Fábio Antunes
显示剩余3条评论
2个回答

1

我实现了一个基本类,可以让你开始工作。我相信一个完全功能的、生产就绪的、线程安全的类需要更多的工作,而且你需要实现自己的策略来轮询值的变化。

public class TargettedObserver<T>
{
    private static readonly EqualityComparer<T> EqualityComparer = EqualityComparer<T>.Default;

    private Func<T> ValueTarget;
    private T OldValue;

    public event ObservedValueChangedEventHandler<T> ValueChanged;

    public TargettedObserver(Func<T> valueTarget)
    {
        this.ValueTarget = valueTarget;
        OldValue = ObtainCurrentValue();
    }

    public bool CheckValue()
    {
        T oldValue = OldValue;
        T newValue = ObtainCurrentValue();

        bool hasValueChanged = CompareValues(oldValue, newValue);

        if (hasValueChanged)
        {
            OldValue = newValue;
            NotifyValueChanged(oldValue, newValue);
        }

        return hasValueChanged;
    }

    private void NotifyValueChanged(T oldValue, T newValue)
    {
        var valueChangedEvent = ValueChanged;
        if (valueChangedEvent != null)
            valueChangedEvent(this, new ObservedValueChangedEventArgs<T>(oldValue, newValue));
    }

    private static bool CompareValues(T oldValue, T newValue)
    {
        return !EqualityComparer.Equals(oldValue, newValue);
    }

    private T ObtainCurrentValue()
    {
        return ValueTarget();
    }
}

还有事件处理:

public class ObservedValueChangedEventArgs<T> : EventArgs
{
    public T OldValue { get; private set; }
    public T NewValue { get; private set; }

    public ObservedValueChangedEventArgs(T oldValue, T newValue)
    {
        this.OldValue = oldValue;
        this.NewValue = newValue;
    }
}

public delegate void ObservedValueChangedEventHandler<T>(TargettedObserver<T> observer, ObservedValueChangedEventArgs<T> eventArgs);

使用方法大致如下:

public class TestClass
{
    private Socket MySocket;
    private static TargettedObserver<bool> SocketConnectedObserver;

    public void Main()
    {
        MySocket = new Socket();
        SocketConnectedObserver = new TargettedObserver<bool>(() => MySocket.Connected);
        SocketConnectedObserver.ValueChanged += ReportSocketConnectedStateChanged;
        PerformSocketConnection();

        MainThread.Invoke(PollSocketValue);
    }

    private void PollSocketValue()
    {
        SocketConnectedObserver.CheckValue();
        MainThread.Invoke(PollSocketValue);
    }

    private void ReportSocketConnectedStateChanged(TargettedObserver<bool> observer, ObservedValueChangedEventArgs<bool> eventArgs)
    {
        Console.WriteLine("Socket connection state changed!  OldValue: " + eventArgs.OldValue + ", NewValue: " + eventArgs.NewValue);
    }
}

请注意构造函数接受一个简单的 lambda 表达式,可以评估您想要观察的值。

还要注意,MainThread.Invoke只是一个伪代码,用于显示它在每个主线程循环中轮询更改。我相信还有更好的策略(例如带有计时器间隔的后台线程)可以以一种好的、可重用的方式实现。至少还有更多的工作要做,以注销观察者。可能可以制作一些漂亮的工厂方法或 lambda 委托,这样您就不需要保持 TargettedObserver 实例浮动,并减少布线/手动编码的数量。但至少这应该是一个开始。


这似乎是一个不错的开端。 我正在考虑基于你的类构建一个类,它将在单个后台线程上管理我分配给它的所有属性的轮询和检查。我有很多要管理的属性,这样一个单独的线程就足以轮询和检查它们了。 此外,我可能会稍微更改一下检查方法,以便在检测到属性主机对象已被处理或设置为null时,不轮询或检查其分配的属性,并将其从检查列表中删除。 听起来像个计划。感谢Chris Sinclair的帮助。 - Fábio Antunes
哦,顺便提一下。你的代码到目前为止非常好。谢谢。 - Fábio Antunes

0
你需要的是观察者模式的实现。类似于这个Observable<T>实现可能会起作用。
另外,在.NET 4中还有IObserver<T>接口

IObserver<T>IObservable<T>接口提供了一种通用的基于推送的通知机制。 IObservable<T>接口表示发送通知的类(提供程序);IObserver<T>接口表示接收通知的类(观察者)。T表示提供通知信息的类。


谢谢Robert,我现在正在尝试你的方法。 - Fábio Antunes
我不确定,但我猜测IObservable<T>实现只适用于我的自定义类,而不适用于.NET Framework中那些不可更改的类,比如Socket。你能否提供一个以Socket类为例子的示例? - Fábio Antunes
我对CodeProject的实现也不确定(因为我从未真正使用过它),但.NET 4类被明确标记为代理类,允许观察现有类而无需实现“INotifyPropertyChanged”。CodeProject文章也提出了同样的说法。 - Robert Harvey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接