views:

43

answers:

2

Consider the following code:

public class Bar {
    Foo foo;
    void Go() {
       foo = new Foo();
       foo.Send(...);

       foo.Dispose();
       foo = null; 
    }
}

public class Foo : IDisposable {   
    public void Send(byte[] bytes) {
        SocketAsyncEventArgs args = new SocketAsyncEventArgs();
        args.SetBuffer(bytes, 0, bytes.Length);
        args.UserToken = socket;
        args.RemoteEndPoint = endPoint;
        args.Completed += new EventHandler<SocketAsyncEventArgs>(OnSendCompleted);

        socket.SendAsync(args);
    }

    private void OnSendCompleted(object sender, SocketAsyncEventArgs e) {
        Debug.WriteLine("great");
    }

    public void Dispose() {
       //
    }
}

So class Bar runs the Init method which instantiates the Foo class and fires off the Send method, then destroys the Foo instance. The Send method meanwhile instantiates a method level SocketAsyncEventArgs, sets up a Completed event and then fires off the SendAsync method.

Assuming that the SendAsync completes after the Foo instance has been set to null, what happens to the event handler? Does it still fire? If I don't want it to fire, how do I properly clean up the Foo class, knowing that a method level variable spawned off an event.

+3  A: 

Yes, it will still fire. Setting a variable to null doesn't trigger garbage collection or anything like that. It just sets the variable to null. (It's important to differentiate between a variable and an instance. There's no such concept as "setting an instance to null". If I write my home address down on a piece of paper, then rub it out again, that doesn't destroy my house.)

It sounds like you may want your Dispose method to "remember" that the object has been cleaned up, and then if OnSendCompleted is called after disposal, just ignore it. Alternatively, keep track of any "requests in flight" and cancel them in in Dispose... noting that some requests might complete while you're cancelling the whole lot.

Another point to note: instead of explicitly calling Dispose(), you should almost always use a using statement, which will make sure that Dispose() is called however the using statement ends (e.g. with an exception).

Jon Skeet
+1  A: 

What about trying to unhook the event by using -= in your OnSendCompleted method ?

e.Completed -= new EventHandler<SocketAsyncEventArgs>(OnSendCompleted);
controlbreak