Есть два объекта 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);
}
}
Вывод будет следующим.
Observer X: The value of Notifier A changed from 0 to 123.
Observer Y: The value of Notifier A changed from 0 to 123.
Observer Y: The value of Notifier B changed from 0 to 999.
Observer X: The value of Notifier A changed from 123 to 1.
Observer Y: The value of Notifier A changed from 123 to 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()
.