This has turned out to be quite a lengthy question, so thank you in advance to all those who give up their time to read it and comment/answer :)
I am using an observer pattern implemented through interfaces:
public interface IObserver<in T>where T:EventArgs
{
void Update(object sender, T e);
}
public interface ISubject<in T, TU>where TU:EventArgs
{
event EventHandler<TU> Notify;
T State { set; }
void Attach(Action<object,TU> callback);
void Detach(Action<object, TU> callback);
}
I have created two simple classes which implement these interfaces
The MyObserver
object will simply output a string to the console window when a Notify
event is raised in the MySubject
object.
public class MyObserver:IObserver<TestEventArgs>
{
private ISubject<bool, TestEventArgs> _subject;
public MyObserver(ISubject<bool, TestEventArgs> subject)
{
_subject = subject;
}
public void Subscribe()
{
_subject.Attach(Update);
}
public void Unsubscribe()
{
_subject.Detach(Update);
}
public void Update(object sender, TestEventArgs e)
{
Console.WriteLine(e.TestMessage);
}
}
public class MySubject:ISubject<bool, TestEventArgs>
{
public void ObservableEvent(string message)
{
InvokeNotify(message);
}
private void InvokeNotify(string message)
{
EventHandler<TestEventArgs> handler = Notify;
if(handler != null)
{
handler(this, new TestEventArgs(message));
}
}
public event EventHandler<TestEventArgs> Notify;
public bool State
{
set { throw new NotImplementedException(); }
}
public void Attach(Action<object, TestEventArgs> callback)
{
Notify += new EventHandler<TestEventArgs>(callback);
}
public void Detach(Action<object, TestEventArgs> callback)
{
Notify -= new EventHandler<TestEventArgs>(callback);
}
}
public class TestEventArgs:EventArgs
{
public TestEventArgs(string message)
{
TestMessage = message;
}
public string TestMessage { get; private set; }
}
This test program shows that:
myObserver
has subscribed to the event no message is output to the Console window.myObserver
has subscribed to the Notify
event the message is output to the Console window.after myObserver
has UNsubscribed from the Notify
event the message is still output to the Console window
static void Main(string[] args)
{
MySubject mySubject = new MySubject();
MyObserver myObserver = new MyObserver(mySubject);
//we have not subscribed to the event so this should not be output to the console
mySubject.ObservableEvent("First Test");
myObserver.Subscribe();
//we are now subscribing to the event. This should be displayed on the console window
mySubject.ObservableEvent("Second Test");
myObserver.Unsubscribe();
//We have unsubscribed from the event. I would not expect this to be displayed
//...but it is!
mySubject.ObservableEvent("Third Test");
Console.ReadLine();
}
The issue I'm having is that the unsubscribe process is not working.
I really don't understand why.
Contains
method always returns false
.I suspect your problem is that this code:
public void Attach(Action<object, TestEventArgs> callback)
{
Notify += new EventHandler<TestEventArgs>(callback);
}
Actually allocates a new object, as does the corresponding Detach
code. So what's being detached isn't the same thing as what's being attached.
I'm not sure, but you might be able to fix it by changing your Attach
and Detach
so that they're:
void Attach(EventHandler<TU> callback);
void Detach(EventHandler<TU> callback);
And in the client code:
public void Attach(EventHandler<TestEventArgs> callback)
{
Notify += callback;
}
public void Detach(EventHandler<TestEventArgs> callback)
{
Notify -= callback;
}
I haven't actually tried to compile this, but it looks like it should work.
Or, if the compiler can do the type conversion:
public void Attach(Action<object, TestEventArgs> callback)
{
Notify += callback;
}
Might be worth a shot.