问题
How to implement the Dispose pattern when my class contains a socket & event?
Should it be something like this?
class MyClass
{
Socket m_ListenerSocket = new Socket();
book m_Disposed=false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool isDisposing)
{
if (!m_Disposed)
{
if (isDisposing)
{
if (m_ListenerSocket != null)
{
m_ListenerSocket.Dispose();
innerClass.Notify -= Notify;
}
}
//finalized unmanged code here
m_Disposed = true;
}
}
~MyClass()
{
Dispose(false);
}
}
I'm confused... is socket class is "managed code c# version of winSock"? so it should be released in case of user called dispose ("isDisposing IS true") what about event handlers?
so at finalized comment section should free only Inptr objects? Thanks.
回答1:
I think there are many ways to deal with disposable objects, regardless if they have events or not.
Is only a wild guess, but if you took a class from the .net framework and this class has a Dispose() method, you can pretty much say that it is managed code, so you are safe just to call the Dispose method instead of creating the destructor by yourself. This is rather obiquitous because as you see in my example below, you can also implement IDisposable interface in your own classes and then dispose your internal objects in a proper way.
Could this be helpful to you?
public class MyClassWithSocket :IDisposable
{
Socket myInternalSocket = null;
public void methodThatUsesSocket()
{
using (var mySocket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream , ProtocolType.Tcp))
{
//do something with socket
//this will be disposed automatically
}
}
public void methodThatUsesInternalSocket()
{
myInternalSocket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
//do other things
}
public static Socket SomethingThatReturnsSocket()
{
Socket tempSocket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
return tempSocket;
}
public void Dispose()
{
myInternalSocket.Dispose();
}
}
回答2:
Since Socket is a managed class, the guidelines for implementing IDisposable states that no Finalizer is needed. In fact, having a finalizer will actually cause the Garbage Collection of the object to be delayed, since it will call the finalizer during the first garbage collection and garbage collect the object the second time garbage collection is run.
Regarding the event you'll probably want to de-register from the event in the Dispose
method, since the event subscription will cause innerClass
to hold a reference to the instance of MyClass
, unless the innerClass object is short lived. See this question for more info on events and Dispose.
I think that the following implementation would be enough for your scenario:
class MyClass : IDisposable
{
Socket m_listenerSocket = new Socket();
public void Dispose()
{
m_listenerSocket.Dispose();
innerClass.Notify -= Notify;
}
}
The part in your code that you have commented with "finalized unmanged code here" should only release unmanaged resources, such as handles in the form of IntPtr
, etc. Since the introduction of SafeHandle in .NET 2.0, you rarely need to do so, since you can wrap your IntPtr
in aSafeHandle
and then treat it as a managed resource.
回答3:
Thou shalt never let anything go unasserted.
In my experience the widely used "soft error" Dispose(disposing)
mechanism is ill conceived. I prefer "hard error".
When I write objects that implement Dispose()
I usually extend a MyDisposable
class which, #if DEBUG
does the following:
- Its constructor captures the current stack trace and saves it for later. (Note: this is extremely slow, Microsoft only knows why.)
- The class contains a
bool disposed
member initialized tofalse
. Dispose()
asserts that the object has not already been disposed, and then marks the object as disposed.- The destructor of the object (invoked upon finalization) checks whether the object has been disposed, and if not, dumps the stack trace recorded during creation. This allows me to locate the exact spot in my source code which allocated a disposable object but forgot to dispose it.
Also, objects derived from MyDisposable
usually assert !disposed
as a precondition in every single method, even every single getter.
The above guarantees that:
- There is only one way in which an object can be disposed.
- Each disposable object will be disposed once and only once.
- No method of a disposable object will ever be invoked once the object has been disposed.
- If I ever forget to dispose a disposable object, I will find out, and I will know exactly where it was allocated.
It goes without saying that if not #DEBUG
then MyDisposable
compiles to practically nothing, so as not to hinder performance.
Consequently, I try to avoid (when practical) using classes that implement the "soft error" Dispose(disposing)
mechanism without first wrapping them in classes implementing my "hard error" Dispose()
mechanism.
回答4:
The existing answers say it already but they are rather verbose. Let me state more clearly that this is all you need:
public void Dispose(){
m_ListenerSocket.Dispose();
}
This is not abridged. You can delete everything else.
No need for a finalizer. No need for null tests. You can dispose multiple times safely.
The "dispose pattern" exists for finalizable objects (extremely rare) and for inheritance (rare). Almost always this pattern does not help at all but damages code quality.
来源:https://stackoverflow.com/questions/13356924/the-correct-technique-for-releasing-a-socket-event-ummaged-code-with-the-dispose