By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
440,407 Members | 1,753 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 440,407 IT Pros & Developers. It's quick & easy.

Unexpected behavior on exception

P: n/a
In C# (and C++/cli) the destructor will be called even if an exception
is thrown in the constructor. IMHO, this is unexpected behavior that can
lead to an invalid system state. So beware!

http://www.geocities.com/jeff_louie/oop30.htm

Regards,
Jeff

*** Sent via Developersdex http://www.developersdex.com ***
Jan 2 '06 #1
Share this Question
Share on Google+
9 Replies


P: n/a
Jeff Louie <je********@yahoo.com> wrote:
In C# (and C++/cli) the destructor will be called even if an exception
is thrown in the constructor. IMHO, this is unexpected behavior that can
lead to an invalid system state. So beware!

http://www.geocities.com/jeff_louie/oop30.htm


Why does the page say it's "non-standard" behaviour? I suspect by that
you mean "not as unmanaged C++ works" which isn't the same thing at
all. It's standard behaviour in terms of it being the behaviour the CLI
standard would suggest. (The object has been created, after all.)

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Jan 2 '06 #2

P: n/a
Hi Jon... I reworded the statement. But it is not only C++ that enforces
this idiom. Even the lowly PHP enforces this idiom. Knowing that the
destructor will only be called on a fully constructed object is integral
to the RAII idiom. IMHO it makes sense that if the _user_ defined
constructor code does not execute to completion then the _user_ defined
destructor code should not execute. This does not stop fully constructed
base objects from calling _their_ destructors. so if [[DefaultObject
alloc]init] succeeds but myObject = [[[DefaultObject alloc]init]myInit]
fails, go ahead and call the destructor of DefaultObject, but _don't_
call the destructor of myObject.

Regards,
Jeff

*** Sent via Developersdex http://www.developersdex.com ***
Jan 3 '06 #3

P: n/a
Jeff Louie wrote:
Hi Jon... I reworded the statement. But it is not only C++ that enforces
this idiom. Even the lowly PHP enforces this idiom. Knowing that the
destructor will only be called on a fully constructed object is integral
to the RAII idiom.
I don't see that it's that integral - it's easy enough to guard against
trying to release resources twice.
IMHO it makes sense that if the _user_ defined
constructor code does not execute to completion then the _user_ defined
destructor code should not execute. This does not stop fully constructed
base objects from calling _their_ destructors. so if [[DefaultObject
alloc]init] succeeds but myObject = [[[DefaultObject alloc]init]myInit]
fails, go ahead and call the destructor of DefaultObject, but _don't_
call the destructor of myObject.


Hmm. "Partially-constructed" objects still need to be garbage
collected, however - so why would they not need to be finalized? What
if the base class constructor succeeds, but the derived class
constructor then fails - would you expect the base class finalizer to
be called, but not the derived class finalizer?

The current behaviour seems consistent to me - you don't need any
special rules about when an object is deemed to be "finalizable". A
"partially-constructed" object is still an object - and may indeed be
used elsewhere, if a constructor (say) adds "this" to a list of
objects, or keeps a static reference somewhere. (Yes, those are both a
bad idea - just listing possibilities :)

In both cases you have to be careful - in the C++ semantics you need to
make absolutely sure that you release any resources acquired during
construction if an exception is thrown. In the CLR semantics you need
to make sure that you don't try to release something that was never
acquired (or which has already been released). I see the latter as
easier though, as there's a single finalizer but there may be many
constructors. Better to only have to be careful in one place :)

Jon

Jan 3 '06 #4

P: n/a

"Jeff Louie" <je********@yahoo.com> wrote in message
news:%2****************@TK2MSFTNGP11.phx.gbl...
Hi Jon... I reworded the statement. But it is not only C++ that enforces
this idiom. Even the lowly PHP enforces this idiom. Knowing that the
destructor will only be called on a fully constructed object is integral
to the RAII idiom. IMHO it makes sense that if the _user_ defined
constructor code does not execute to completion then the _user_ defined
destructor code should not execute. This does not stop fully constructed
base objects from calling _their_ destructors. so if [[DefaultObject
alloc]init] succeeds but myObject = [[[DefaultObject alloc]init]myInit]
fails, go ahead and call the destructor of DefaultObject, but _don't_
call the destructor of myObject.

Regards,
Jeff

*** Sent via Developersdex http://www.developersdex.com ***


If an exception propagates out of a constructor, you'll have to make sure
that your destructor (Finalize) can handle "partially contructed" objects.
Another option is to catch exceptions in the contructor and suppress
finalization, like:

class Foo {
public Foo()
{
try {
FunctionThatCanThrow();
}
catch(Exception) {
GC.SuppressFinalize(this);
throw;
}
resourceHandle = ....// allocate unmanaged resource that needs
clean-up.
}
~Foo()
{
if(resourceHandle)
... // clean-up
}

Willy.

Jan 3 '06 #5

P: n/a
In addition to the comments and options Jon and Willy offered, you can also
separate the construction of the object from the acquisition of resources
into two separate phases so that the constructor never throws an exception.
In the constructor set fields to known values (null, etc) to estiablish its
invariants, then use an Initialization method to acquire the resources.

FYI: If an exception is thrown from a static constructor then that type is
thereafter unusable in that appdomain - you will not be able to create
instances of that type in that AppDomain.

"Jeff Louie" <je********@yahoo.com> wrote in message
news:%2****************@TK2MSFTNGP11.phx.gbl...
Hi Jon... I reworded the statement. But it is not only C++ that enforces
this idiom. Even the lowly PHP enforces this idiom. Knowing that the
destructor will only be called on a fully constructed object is integral
to the RAII idiom. IMHO it makes sense that if the _user_ defined
constructor code does not execute to completion then the _user_ defined
destructor code should not execute. This does not stop fully constructed
base objects from calling _their_ destructors. so if [[DefaultObject
alloc]init] succeeds but myObject = [[[DefaultObject alloc]init]myInit]
fails, go ahead and call the destructor of DefaultObject, but _don't_
call the destructor of myObject.

Regards,
Jeff

*** Sent via Developersdex http://www.developersdex.com ***

Jan 4 '06 #6

P: n/a
Willy and MSFT... I added your suggestions to the article.

Thanks,
Jeff

*** Sent via Developersdex http://www.developersdex.com ***
Jan 8 '06 #7

P: n/a
On Sun, 01 Jan 2006 19:32:15 -0800, Jeff Louie <je********@yahoo.com>
wrote:
In C# (and C++/cli) the destructor will be called even if an exception
is thrown in the constructor. IMHO, this is unexpected behavior that can
lead to an invalid system state. So beware!


Unexpected to you perhaps. I can't see anything else happening as
preferable. If an object gets half way through construction and
fails for /any/ reason, I would certainly want the destructor called
to clean up any partially constructed stuff and release any partially
aquired resources.

Oz
--
A: Because it fouls the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jan 8 '06 #8

P: n/a
Hello ozbear,
Unexpected to you perhaps. I can't see anything else happening as
preferable. If an object gets half way through construction and fails
for /any/ reason, I would certainly want the destructor called to
clean up any partially constructed stuff and release any partially
aquired resources.


I agree. It can be quite awkward at first but taken the other way you would
have to implement a destructor twice. Once in the destructor and once in
the constructor to handle any un-expected errors.

--
Jared Parsons
ja****@beanseed.org
http://jaredparsons.blogspot.com
Jan 8 '06 #9

P: n/a
Ozbear... It's not that simple. First, say a reference counter is
incremented in
the constructor. In standard C++ I know that the destructor will only be
called if the constructor completes successfully so that I know that I
can
decrement the reference count in the destructor. Period. In C# there is
no
such guarantee. I need to code around this. If I don't, I could
decrement the
reference counter in the destructor and another object can try to call a
method on a prematurely released resource.

Secondly, there is _no_ guarantee that the destructor will be called in
a timely
manner! If an exception is thrown in the constructor in a using
construct,
Dispose is _not_ called. The resource will only be released when the
garbage
collector gets around to it.

So IMHO, cleaning up the resources in the constructor on exception and
then
call GC.SuppressFinalize(this) is the preferable solution for
deterministic
release of unmanaged resources.

Regards,
Jeff
Unexpected to you perhaps. I can't see anything else happening as

preferable. If an object gets half way through construction and
fails for /any/ reason, I would certainly want the destructor called
to clean up any partially constructed stuff and release any partially
aquired resources.<

*** Sent via Developersdex http://www.developersdex.com ***
Jan 9 '06 #10

This discussion thread is closed

Replies have been disabled for this discussion.