有两个对象NOTIFIER 和OBSERVER。NOTIFIER 对 OBSERVER 一无所知,而 OBSERVER 知道 NOTIFER 实现了一个事件。
OBSERVER 使用该事件通知其他对象发生了什么事。简单地说,一个事件就是一个方法列表。因为 OBSERVER 希望在发生某事时得到通知,所以 OBSERVER 为 NOTIFER 事件添加了一个在发生某事时应该调用的方法。
所以如果事情发生了,NOTIFIER 发布了这个事件,NOTIFIER 只是遍历方法列表并调用它们。当调用 OBSERVER 添加的方法时,OBSERVER 就知道事情发生了,并且可以在这种情况下做任何需要的事情。
这是一个带有ValueChanged()
事件的示例通知程序类。
// Declare how a method must look in order to be used as an event handler.
public delegate void ValueChangedHandler(Notifier sender, Int32 oldValue, Int32 newValue);
public class Notifier
{
// Constructor with an instance name.
public Notifier(String name)
{
this.Name = name;
}
public String Name { get; private set; }
// The event that is raised when ChangeValue() changes the
// private field value.
public event ValueChangedHandler ValueChanged;
// A method that modifies the private field value and
// notifies observers by raising the ValueChanged event.
public void ChangeValue(Int32 newValue)
{
// Check if value really changes.
if (this.value != newValue)
{
// Safe the old value.
Int32 oldValue = this.value;
// Change the value.
this.value = newValue;
// Raise the ValueChanged event.
this.OnValueChanged(oldValue, newValue);
}
}
private Int32 value = 0;
// Raises the ValueChanged event.
private void OnValueChanged(Int32 oldValue, Int32 newValue)
{
// Copy the event handlers - this is for thread safty to
// avoid that somebody changes the handler to null after
// we checked that it is not null but before we called
// the handler.
ValueChangedHandler valueChangedHandler = this.ValueChanged;
// Check if we must notify anybody.
if (valueChangedHandler != null)
{
// Call all methods added to this event.
valueChangedHandler(this, oldValue, newValue);
}
}
}
这是一个示例观察者类。
public class Observer
{
// Constructor with an instance name.
public Observer(String name)
{
this.Name = name;
}
public String Name { get; private set; }
// The method to be registered as event handler.
public void NotifierValueChanged(Notifier sender, Int32 oldValue, Int32 newValue)
{
Console.WriteLine(String.Format("{0}: The value of {1} changed from {2} to {3}.", this.Name, sender.Name, oldValue, newValue));
}
}
一个小型测试应用程序。
class Program
{
static void Main(string[] args)
{
// Create two notifiers - Notifier A and Notifier B.
Notifier notifierA = new Notifier("Notifier A");
Notifier notifierB = new Notifier("Notifier B");
// Create two observers - Observer X and Observer Y.
Observer observerX = new Observer("Observer X");
Observer observerY = new Observer("Observer Y");
// Observer X subscribes the ValueChanged() event of Notifier A.
notifierA.ValueChanged += observerX.NotifierValueChanged;
// Observer Y subscribes the ValueChanged() event of Notifier A and B.
notifierA.ValueChanged += observerY.NotifierValueChanged;
notifierB.ValueChanged += observerY.NotifierValueChanged;
// Change the value of Notifier A - this will notify Observer X and Y.
notifierA.ChangeValue(123);
// Change the value of Notifier B - this will only notify Observer Y.
notifierB.ChangeValue(999);
// This will not notify anybody because the value is already 123.
notifierA.ChangeValue(123);
// This will not notify Observer X and Y again.
notifierA.ChangeValue(1);
}
}
输出将如下所示。
观察者 X:Notifier A 的值从 0 变为 123。
观察者 Y:Notifier A 的值从 0 变为 123。
观察者 Y:Notifier B 的值从 0 变为 999。
观察者 X:Notifier A 的值从 123 变为 1。
观察者 Y:Notifier A 的值从 123 变为 1。
为了理解委托类型,我将把它们与类类型进行比较。
public class Example
{
public void DoSomething(String text)
{
Console.WriteLine(
"Doing something with '" + text + "'.");
}
public void DoSomethingElse(Int32 number)
{
Console.WriteLine(
"Doing something with '" + number.ToString() + "'.");
}
}
Example
我们用两个方法定义了一个简单的类。现在我们可以使用这个类类型了。
Example example = new Example();
虽然这可行,但以下内容不起作用,因为类型不匹配。你得到一个编译器错误。
Example example = new List<String>();
我们可以使用变量example
.
example.DoSomething("some text");
现在与委托类型相同。首先我们定义一个委托类型——这只是一个类型定义,就像之前的类定义一样。
public delegate void MyDelegate(String text);
现在我们可以使用委托类型,但是我们不能将普通数据存储在委托类型变量中,而是在方法中。
MyDelegate method = example.DoSomething;
我们现在已经存储DoSomething()
了对象的方法example
。以下内容不起作用,因为我们将MyDelegate
委托定义为采用一个字符串参数并返回 void。DoSomethingElse
返回 void 但采用整数参数,因此您会收到编译器错误。
MyDelegate method = example.DoSomethingElse;
最后你可以使用变量method
. 您不能执行数据操作,因为变量不存储数据而是存储方法。但是您可以调用存储在变量中的方法。
method("Doing stuff with delegates.");
这会调用我们存储在变量 - 中的方法example.DoSomething()
。