65.9K
CodeProject 正在变化。 阅读更多。
Home

在类中监控属性值更改

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.31/5 (8投票s)

2012年1月7日

CPOL

3分钟阅读

viewsIcon

25647

downloadIcon

499

一个类,允许你监控变量的实际变化,并通过事件通知所有感兴趣的对象

引言

考虑一下需要监控类属性更改的情况。 每次发生更改时,都应该触发一个事件来通知所有感兴趣的对象。 为此,我开发了一个简单的类,我经常使用它。

这是一个需求的例子:我们有一个类,用于处理数据库,该类经常执行查询。 我们需要监控数据库通道的状态 - 它是 OK 还是 not OK。

背景

为了实现这一点,每次我们成功查询时,我们会查看连接是否 OK,当由于数据库连接失败导致查询失败时,通道状态变为断开连接。

然后我们得到类似这样的代码

public class DBGateway
{
    private bool connectionIsOk = false;

    public bool ConnectionIsOK 
    {
        get { return connectionIsOk; }
        private set 
        {
                        connectionIsOk = value;
            if (connectionIsOk == false && value == true)
                RaiseConnectionRestored();
            if (connectionIsOk == true && value == false)
                RaiseConnectionLost();
        }
    }

    private string connectionString;

    public void ExecuteQuery()
    {
        try
        {
            //...some useful staff: connection to database, and so on
            ConnectionIsOK  = true;  //because we are here, 
                                     //and no exception occured - then it's OK
        }
        catch(SqlException)
        {
            ConnectionIsOK = false;
            throw;
        }
    }

    private void RaiseConnectionRestored()
    {
        if (ConnectionRestored != null) ConnectionRestored(this, EventArgs.Empty);
    }    

    private void RaiseConnectionLost()
    {
        if (ConnectionLost!= null) ConnectionLost(this, EventArgs.Empty);
    }

    public event EventHandler ConnectionRestored;
    
    public event EventHandler ConnectionLost;
}

这段代码的错误之处在于数据库网关类有额外的责任。 我们有两个事件,用于触发这些事件的函数,以及用于检查值是否真正更改的逻辑。

ValueMonitor<T> 类

ValueMonitor是一个简单的类,它封装了更改检查的逻辑。 它有一个ValueChanged事件,一个Value属性,以及一个允许你设置被监控变量初始值的构造函数。

我们需要特别注意值类型和引用类型。 Equals函数返回的正是值类型真正期望的内容。

然而,引用类型相等比较的结果是“指针”比较的结果。 这不是我们真正需要的。 这就是为什么对于引用类型,重载的构造函数版本期望使用IEqualityComparer

ValueMonitorDescription/ClassDiagram.png

考虑值更改检查代码

public class ValueMonitor<ValueType>:IValueMonitor<ValueType>
{
    private ValueType aValue = default(ValueType);
    private IEqualityComparer<ValueType> comparer = null;

    //all other members are ommited ...    

    public ValueType Value
    {
        get { return aValue; }
        set
        {
            bool areEqual = false;
            if (comparer == null)
                areEqual = (aValue.Equals(value));
            else areEqual = comparer.Equals(aValue, value);

            if (areEqual == true) return;
                ValueType oldValue = aValue; // remember previous for the event rising
            aValue = value;
            if (ValueChanged != null)
                ValueChanged(oldValue, aValue);
        }
    }
}

Using the Code

现在我们的DBGateway类的代码将如下所示

public class DBGateway
{
    private ValueMonitor<bool> connectionIsOk = new ValueMonitor<bool>(false);

    public IValueMonitor<bool> ConnectionIsOK 
    {
        get { return connectionIsOk; }
    }

    private string connectionString;

    public void ExecuteQuery()
    {
        try
        {
            //...some useful staff: connection to database, and so on
            connectionIsOk.Value  = true; //because we a here, 
                                          //and no exception occured - then it's OK
        }
        catch(SqlException)
        {
            connectionIsOk.Value = false;
            throw;
        }
    }
}

如你所见,我们摆脱了额外的逻辑,我们的类变得更轻量级,我们现在可以专注于数据库代码而不是值更改监控。

示例应用程序

当然,在示例代码中,我们不监控数据库连接。 为了查看类的运作,用户在Form类中将值truefalse设置为status变量。

表单处理ValueChanged事件并显示变量实际更改的时间。

ValueMonitorDescription/lastUpdated.gif

多次单击同一个按钮没有效果。 更改按钮则会产生效果。

关注点

使用示例

  • 监控任何类型的通道的状态,例如在DBGateway示例中。
  • 监控对象状态的更改。 想象一下我们有一个状态模式的实现,并且我们希望知道当前状态及其变化。 然后我们有一个像这样的枚举
    public enum ObjectState
    {
       Disconnected,
       LoggingIn,
       Normal
    }

    然后,为了提供值更改监控,我们添加类似这样的代码

    ValueMonitor<ObjectState> currentState = 
             new ValueMonitor<ObjectState>(ObjectState.Disconnected);
  • 当你从另一个客户端应用程序接收流量,并且你想监控当前连接的IP地址的情况。 这种情况并不少见:两个主机可以使用不同的通道,当一个失败时,另一个(备用主机)连接,并且系统仍然稳定。

历史

  • 2012年7月1日 - 第一个版本
  • 2012年8月1日 - 在 Ravi Bhavnani 的评论之后,更改了DBGateway的第一个例子。 这一行
    connectionIsOk = true;

    被添加到了ConnectionIsOK属性中。

© . All rights reserved.