What makes a class IDisposable?
-
Are you being purposefully daft? I understand what's going on; I'm arguing that "C# does not have destructors" is an untrue statement. I'm not repudiating the mechanics. Dispute this: The C# language specification allows for the definition of destructors for class types.
From: http://en.wikipedia.org/wiki/Destructor\_(computer\_science) In object-oriented programming, a destructor (sometimes shortened to dtor) is a method which is automatically invoked when the object is destroyed. Its main purpose is to clean up and to free the resources which were acquired by the object along its life cycle and unlink it from other objects or resources invalidating any references in the process. The use of destructors is key to the concept of RAII. How in C# do you declare such a thing? A finalizer is not automatically invoked when the object is destroyed deterministically. It is nondeterministically called by the GC at some point in the future. Most important, in no way does a finalizer enable the RAII pattern. Take a
FileStream
class which encapsulates a resource in C#. Declare a finalizer for it. When an instance of a FileStream class goes out of the scope the finalizer is not called then. You do not know when it is called. Furthermore, even if the GC does a collect and there are no roots in the code to the object, the simple existence of the finalizer counts as a root and can cause theFileStream
object to be promoted into the next generation and not collected by the GC until the next collect cycle (who knows when that will be). So while yourFileStream
object may have gone out of scope ages ago, by not callingDispose
and assuming the finalizer will close theFileStream
, you have potentially caused a resource leak. The next function call that assumes the file you previously had used with theFileStream
object is closed may throw an exception because you were relying on the garbage collector to nondeterministically release a critical resource. On the other hand, a destructor in C++/CLI will be called the moment theFileStream
goes out of scope (assuming you declared the object with stack semantics and notgcnew
). Then in the destructor you make sure there is a call toDispose
and you ensure that the resource is released immediately. You don't need atry/finally
and you don't need to explicitly callDispose
and nor does anyone who uses your class in the future. The destructor incurs no performance penalty during allocation or deallocation like a finalizer does either. There's no way to declare such a function in C#. To say 'I'm not repudiating the mechanics' therefore C# has destructors is completely wrong. The mechanics are exa -
From: http://en.wikipedia.org/wiki/Destructor\_(computer\_science) In object-oriented programming, a destructor (sometimes shortened to dtor) is a method which is automatically invoked when the object is destroyed. Its main purpose is to clean up and to free the resources which were acquired by the object along its life cycle and unlink it from other objects or resources invalidating any references in the process. The use of destructors is key to the concept of RAII. How in C# do you declare such a thing? A finalizer is not automatically invoked when the object is destroyed deterministically. It is nondeterministically called by the GC at some point in the future. Most important, in no way does a finalizer enable the RAII pattern. Take a
FileStream
class which encapsulates a resource in C#. Declare a finalizer for it. When an instance of a FileStream class goes out of the scope the finalizer is not called then. You do not know when it is called. Furthermore, even if the GC does a collect and there are no roots in the code to the object, the simple existence of the finalizer counts as a root and can cause theFileStream
object to be promoted into the next generation and not collected by the GC until the next collect cycle (who knows when that will be). So while yourFileStream
object may have gone out of scope ages ago, by not callingDispose
and assuming the finalizer will close theFileStream
, you have potentially caused a resource leak. The next function call that assumes the file you previously had used with theFileStream
object is closed may throw an exception because you were relying on the garbage collector to nondeterministically release a critical resource. On the other hand, a destructor in C++/CLI will be called the moment theFileStream
goes out of scope (assuming you declared the object with stack semantics and notgcnew
). Then in the destructor you make sure there is a call toDispose
and you ensure that the resource is released immediately. You don't need atry/finally
and you don't need to explicitly callDispose
and nor does anyone who uses your class in the future. The destructor incurs no performance penalty during allocation or deallocation like a finalizer does either. There's no way to declare such a function in C#. To say 'I'm not repudiating the mechanics' therefore C# has destructors is completely wrong. The mechanics are exa -
Well like I said then, make sure to declare
~Class()
in all your C# classes and don't bother explicitly invokingDispose
. I'm sure you'll find it works fantastically. Also make sure to take advantage of finalizers to declare reference classes in C# with stack semantics. -- modified at 15:30 Monday 13th August, 2007 -
Well like I said then, make sure to declare
~Class()
in all your C# classes and don't bother explicitly invokingDispose
. I'm sure you'll find it works fantastically. Also make sure to take advantage of finalizers to declare reference classes in C# with stack semantics. -- modified at 15:30 Monday 13th August, 2007No need to get pissy. As I said earlier, I'm not advocating the use of what the language itself calls (or, apparently, used to call) a destructor; I'm just claiming that such a beast exists. From the C# Language Specification: [Note: In the previous version of this standard, what is now referred to as a "finalizer" was called a "destructor". Experience has shown that the term "destructor" caused confusion and often resulted to incorrect expectations, especially to programmers knowing C++. In C++, a destructor is called in a determinate manner, whereas, in C#, a finalizer is not. To get determinate behavior from C#, one should use Dispose. end note]
-
I'm pretty sure we've said the same thing. However... You say destructors are not called on an object allocated on the managed heap which isn't strictly true. If you allocate it on the managed heap with
gcnew
then yes the destructor is never called if you don't manually calldelete
. However, if you instantiate theref class
object with stack semantics then the destructor is called implicitly when the object goes out of scope (even though underneath its still allocated on the managed heap like allref class
objects). This is the critical addition to C++/CLI (vs MC++) that allowed for RAII style coding like native C++. Michael was claiming this is not the case (as it is in native C++) and that the finalizer is what is called, which is not correct. The finalizer will be called eventually either way if you define one, but the way things work is obviously much different than what a destructor provides. The confusion is that in C# when you declare~Class1()
this is actually mapped to the finalizer (Dispose(false)
) in the dispose pattern. C# has no concept of destructors like C++ or C++/CLI even though the semantics there look like you're declaring a destructor. In C++/CLI you accomplish the same thing by declaring!Class1()
which more properly distinguishes the finalizer from the destructor. Then declaring ~Class1() in C++/CLI does a very different thing than~Class1()
in C#. That's why in C# you need to explicitly callDispose()
in afinally
block or use ausing
block with things likeFileWriter
objects to ensure there is no resource leak. C++/CLI's destructor semantics provide the handy native C++ style,FileWriter fw;
which will automatically be disposed of properly when it goes out of scope (deterministically).iddqd515 wrote:
in C# when you declare ~Class1() this is actually mapped to the finalizer (Dispose(false)) in the dispose pattern.
Dispose() isn't the finalizer...Finalize() is, right? Regardless, C++ does the same - the destructor becomes Dispose() (quite literally, as Michael's original warning message stated). In C++ the Destructor is explicitly called when a stack-based syntax managed object goes out of scope, the destructor is called directly, a managed heap object is explicitly deleted with delete, or another language calls Dispose() on an object. If the class implements a finalizer then that needs to be (at least SHOULD be) called from the destructor. The compiler takes care of calling SuppressFinalize so the finalizer will not be called again by the GC. Isn't that the same as the C# (and probably VB) Dispose/Finalize pattern? Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
No need to get pissy. As I said earlier, I'm not advocating the use of what the language itself calls (or, apparently, used to call) a destructor; I'm just claiming that such a beast exists. From the C# Language Specification: [Note: In the previous version of this standard, what is now referred to as a "finalizer" was called a "destructor". Experience has shown that the term "destructor" caused confusion and often resulted to incorrect expectations, especially to programmers knowing C++. In C++, a destructor is called in a determinate manner, whereas, in C#, a finalizer is not. To get determinate behavior from C#, one should use Dispose. end note]
So that right there says 'sorry we incorrectly used to refer to this as a destructor. We realized it was confusing because its really not a destructor at all. To get destructor like behavior in C# use the Dispose method.' Therefore, C# has no destructors. And even in the past, it only had them in name, not function. But its been awhile since anyone referred to them as destructors since its blatantly confusing, especially with the introduction of C++/CLI in '05.
-
From: http://en.wikipedia.org/wiki/Destructor\_(computer\_science) In object-oriented programming, a destructor (sometimes shortened to dtor) is a method which is automatically invoked when the object is destroyed. Its main purpose is to clean up and to free the resources which were acquired by the object along its life cycle and unlink it from other objects or resources invalidating any references in the process. The use of destructors is key to the concept of RAII. How in C# do you declare such a thing? A finalizer is not automatically invoked when the object is destroyed deterministically. It is nondeterministically called by the GC at some point in the future. Most important, in no way does a finalizer enable the RAII pattern. Take a
FileStream
class which encapsulates a resource in C#. Declare a finalizer for it. When an instance of a FileStream class goes out of the scope the finalizer is not called then. You do not know when it is called. Furthermore, even if the GC does a collect and there are no roots in the code to the object, the simple existence of the finalizer counts as a root and can cause theFileStream
object to be promoted into the next generation and not collected by the GC until the next collect cycle (who knows when that will be). So while yourFileStream
object may have gone out of scope ages ago, by not callingDispose
and assuming the finalizer will close theFileStream
, you have potentially caused a resource leak. The next function call that assumes the file you previously had used with theFileStream
object is closed may throw an exception because you were relying on the garbage collector to nondeterministically release a critical resource. On the other hand, a destructor in C++/CLI will be called the moment theFileStream
goes out of scope (assuming you declared the object with stack semantics and notgcnew
). Then in the destructor you make sure there is a call toDispose
and you ensure that the resource is released immediately. You don't need atry/finally
and you don't need to explicitly callDispose
and nor does anyone who uses your class in the future. The destructor incurs no performance penalty during allocation or deallocation like a finalizer does either. There's no way to declare such a function in C#. To say 'I'm not repudiating the mechanics' therefore C# has destructors is completely wrong. The mechanics are exaiddqd515 wrote:
On the other hand, a destructor in C++/CLI will be called the moment the FileStream goes out of scope (assuming you declared the object with stack semantics and not gcnew). Then in the destructor you make sure there is a call to Dispose
That may be where my confusion with your statements is coming from. That sounds like managed extensions. In VS 2005, you can't call Dispose() - the compiler won't let you. This is now done implicitly with the destructos symantics. Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
iddqd515 wrote:
On the other hand, a destructor in C++/CLI will be called the moment the FileStream goes out of scope (assuming you declared the object with stack semantics and not gcnew). Then in the destructor you make sure there is a call to Dispose
That may be where my confusion with your statements is coming from. That sounds like managed extensions. In VS 2005, you can't call Dispose() - the compiler won't let you. This is now done implicitly with the destructos symantics. Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
Sorry, I was being ambiguous. All this Dispose talk and switching between C# and C++/CLI gets me mixed up occasionally. You're correct, I meant in the destructor a call to
Dispose(true)
is made. In any case, the important part (C++/CLI destructors are deterministic) is the same, while a C# finalizer is completely different due to its nondeterministic nature. Luckily I've never had to use MC++. *shudder* -- modified at 15:49 Monday 13th August, 2007 -
Sorry, I was being ambiguous. All this Dispose talk and switching between C# and C++/CLI gets me mixed up occasionally. You're correct, I meant in the destructor a call to
Dispose(true)
is made. In any case, the important part (C++/CLI destructors are deterministic) is the same, while a C# finalizer is completely different due to its nondeterministic nature. Luckily I've never had to use MC++. *shudder* -- modified at 15:49 Monday 13th August, 2007Cool. I'm just making sure for my own clarification. I recently ported code from VS2003 and while I was ready for many of the changes, the Dispose stuff was a surprise. I want to make sure I've got it clear in my head since I only used the documentation as a learnin' resource :) Cheers, Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
Cool. I'm just making sure for my own clarification. I recently ported code from VS2003 and while I was ready for many of the changes, the Dispose stuff was a surprise. I want to make sure I've got it clear in my head since I only used the documentation as a learnin' resource :) Cheers, Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
Yeah its taken me over a month to really grasp the whole pattern and how it meshes with traditional C++ semantics. Its not helpful when you try to find stuff about the Dispose pattern and end up with C# examples mixing terms to create more confusion. On the plus side, now C++/CLI basically works just like you would expect native C++ to in most cases and you only need to worry about finalizers for classes with unmanaged members. Certainly an improvement over MC++ (as most things are).
-
Michael Chapman wrote:
That just seems totally counterintuitive to using managed code.
Yes, but so is mixing native types and having destructors. The garbage collector will only cleanup managed objects if you don't specifically call a destructor. Since you don't need deterministic cleanup of your native object, the smart pointer is a good solution, since it provides the destructor/finalizer for you. Only if you needed deterministic cleanup of your native object would you need to implement a destructor and possibly a finalizer as shown in the article (although you do it implicitely with the smart pointer). Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
Mark, You might also want to consider
SafeHandle
: http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx[^]"We make a living by what we get, we make a life by what we give." --Winston Churchill
-
Mark, You might also want to consider
SafeHandle
: http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx[^]"We make a living by what we get, we make a life by what we give." --Winston Churchill
Interesting! Thanks for the link George! Mark
Mark Salsbery Microsoft MVP - Visual C++ :java: