在类中监控属性值更改
一个类,允许你监控变量的实际变化,并通过事件通知所有感兴趣的对象
引言
考虑一下需要监控类属性更改的情况。 每次发生更改时,都应该触发一个事件来通知所有感兴趣的对象。 为此,我开发了一个简单的类,我经常使用它。
这是一个需求的例子:我们有一个类,用于处理数据库,该类经常执行查询。 我们需要监控数据库通道的状态 - 它是 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
。
考虑值更改检查代码
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
类中将值true
和false
设置为status
变量。
表单处理ValueChanged
事件并显示变量实际更改的时间。
多次单击同一个按钮没有效果。 更改按钮则会产生效果。
关注点
使用示例
- 监控任何类型的通道的状态,例如在
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
属性中。