473,545 Members | 1,744 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Exception Safety in C#

Hello:

As an avid reader of C++ books, I know a lot of programmers out there
are confronted with the challenge of exception safety.

For those who don't know what it is, it is writing code in such a way
that an exception will not cause a class to be left in an unstable
state. For instance, you wouldn't want an OutOfMemoryExce ption leaving
a container with an invalid size or missing elements.

Now, in C++, the main idea is to write code so that you do as much
work as possible withouth doing anything that can cause and exception,
and back track if something does go wrong. In C++, implementors
usually do this by using an idiom called, copy/modify/swap, to achieve
exception safety.

I am wondering if this is easier to do in C#. Assignment in C# has a
different meaning than assignment in C++ for reference types.
Assignment makes things equal, not equivelent. C# natively supports
Clone(), but not really a copy ctor. Since C# uses references, I can't
really change my reference since multiple references may be pointed to
the object I am modifying (some times).

Imagine there is a class that looks something like this:

class Dog
{
private int age = 0;
// . . .
public void CelebrateBirthd ay()
{
++age;
// . . . something throws an exception at this point
}
}

If I were to execute this code and an exception was thrown, my dog
would be a year older than he really should be (assuming that I want
to rollback (undo) the change in the face of an exception). One
solution would look like this:

public void CelebrateBirthd ay()
{
try
{
++age;
// . . . something throws an exception at this point
}
catch
{
--age;
// undo potential changes to other fields (may require storing
the old value)
}
}

Now, I would have to write that around every mutating method/property
in my class. YUCK! In a class with dozens of fields, that would be a
nightmare.

This is my proposed solution, similar to how it is done is C++:

private void copy(Dog dog)
{
age = dog.age;
// transfer additional fields
}

public void CelebrateBirthd ay()
{
Dog tempDog = new Dog();
tempDog.copy(th is);
++tempDog.age;
// . . . something throws an exception at this point
this.copy(tempD og);
}

Notice that there is no longer a need for try/catch. If something goes
wrong, the copy, tempDog, just gets wasted. The final line is to
update 'this' with the changes.

Now, this seems really simple to me. I have trouble believing that
someone didn't come up with something more general or correct (I think
mine is correct???). For instance, there is a lot to be concerned
about with deep/shallow copies.

Community-wide approaches are what I am looking for. Anyone?

Thanks,
Travis Parks

Jun 29 '07 #1
14 2094
On Thu, 28 Jun 2007 18:54:44 -0700, je**********@gm ail.com
<je**********@g mail.comwrote:
[...]
Notice that there is no longer a need for try/catch. If something goes
wrong, the copy, tempDog, just gets wasted. The final line is to
update 'this' with the changes.

Now, this seems really simple to me. I have trouble believing that
someone didn't come up with something more general or correct (I think
mine is correct???). For instance, there is a lot to be concerned
about with deep/shallow copies.
I'm afraid I don't really understand the question. The technique -- to
create a temporary instance on which one operates and then somehow replace
the original with the temporary version -- you describe is both general
and widespread. It applies in a number of scenarios, and not just in OOP
classes (for example, writing to a temp file, and only deleting the
original and renaming the temp file if the temp file is successfully and
completely created).

That said, C++ does not inherently support this technique any more than C#
does. So I don't really understand the comparison you seem to be making
between C++ and C#.

As far as whether the technique is appropriate generally, I'd say much of
the time it's not. It really depends on what you're doing, but creating a
complete copy of an instance, modifying, and then copying all of that back
to the original has a lot of overhead and itself requires implementation
of the Copy() method or something like it. I have yet to run into a
situation where I felt it was unreasonable to simply do whatever cleanup I
need to do locally should an exception occur.

Note also that in your example, you sort of gloss over the fact that the
exception still needs to be caught somewhere. Yes, you can catch it and
just ignore it. But if you're going to have to write the exception
handler anyway, why not have it do something useful, like clean up after
whatever operation the code contained within was doing?

For those times when the technique is appropriate, then sure...I'd agree
that it's, well...appropri ate. Would I write it into every one of my
classes as a standard matter? Nope.

Pete
Jun 29 '07 #2
On Jun 28, 8:48 pm, "Peter Duniho" <NpOeStPe...@nn owslpianmk.com>
wrote:
On Thu, 28 Jun 2007 18:54:44 -0700, jehugalea...@gm ail.com

<jehugalea...@g mail.comwrote:
[...]
Notice that there is no longer a need for try/catch. If something goes
wrong, the copy, tempDog, just gets wasted. The final line is to
update 'this' with the changes.
Now, this seems really simple to me. I have trouble believing that
someone didn't come up with something more general or correct (I think
mine is correct???). For instance, there is a lot to be concerned
about with deep/shallow copies.

I'm afraid I don't really understand the question. The technique -- to
create a temporary instance on which one operates and then somehow replace
the original with the temporary version -- you describe is both general
and widespread. It applies in a number of scenarios, and not just in OOP
classes (for example, writing to a temp file, and only deleting the
original and renaming the temp file if the temp file is successfully and
completely created).

That said, C++ does not inherently support this technique any more than C#
does. So I don't really understand the comparison you seem to be making
between C++ and C#.

As far as whether the technique is appropriate generally, I'd say much of
the time it's not. It really depends on what you're doing, but creating a
complete copy of an instance, modifying, and then copying all of that back
to the original has a lot of overhead and itself requires implementation
of the Copy() method or something like it. I have yet to run into a
situation where I felt it was unreasonable to simply do whatever cleanup I
need to do locally should an exception occur.

Note also that in your example, you sort of gloss over the fact that the
exception still needs to be caught somewhere. Yes, you can catch it and
just ignore it. But if you're going to have to write the exception
handler anyway, why not have it do something useful, like clean up after
whatever operation the code contained within was doing?

For those times when the technique is appropriate, then sure...I'd agree
that it's, well...appropri ate. Would I write it into every one of my
classes as a standard matter? Nope.

Pete
My original post asks for a community-wide approach to exception
safety. I wasn't really concerned with the differences between
languages or how the copy/modify/replace idiom applies to various
other aspects of computer science. I simply want to know what other
developers have done to face the problem.

I don't agree that catch blocks should be utilized to undo what the
try was trying to. It is probably one of the most efficient means.
However, programmers are generally forgetful. Having to control that
depth of code inside potentially many catch blocks is a maintenance
nightmare. You would probably create a single method that resets the
values. But then you need to know what to reset them to. So you need
to store those values somewhere.

I am just suggesting (as it makes sense to group related data) to
create a copy. You don't have to muddy up your code with try/catch
then. And I am not all too concerned about handling the exceptions. As
such, I don't even want to make my code *appear* to be concerned about
them; it should just do the right thing.

I don't think the implementation of a copy() method would be a big
chore. You saw that it can be fairly easy. You are just saving your
state. assignment assignment assignment . . .

Now here is the dilemma . . . since we created a copy of our class, we
cannot call any methods that create a copy of our class without
redundant copies being made. So we have to assume that we are only
dealing with the fields directly or methods that are not exception
safe. It seems like a huge limitation. However, I think this presents
a wonderful opportunity! I think this suggests that we create
interface methods and implementation methods, where one simply
delegates to the other. We can then formalize the copy/modify/replace
process.

public class Dog
{
public void CelebrateBirthd ay()
{
Dog tempDog = new tempDog(); // all public modifying methods
begin and end the same way
tempDog.celebra teBirthday(); // implementation method
this.copy(tempD og);
}
}

Now, personally, I do not think copying is a large waste. We are
talking about just a few bytes for typical classes (most of which
would need to be stored to undo changes anyway). We could probably
even get away with reduntant copies if we wanted to. It depends how
anal we are about efficiency. If we truly wanted, we could even make
the state of the class a private struct with public fields and leave
ourselves open to enter the realm of the state pattern (if needed) and
just copy/modify/replace it.

We need to store the original state somehow, somewhere. We originally
created the class to enclose that data. Why not use it?

Now, from my experience, I rarely deal with exceptions locally - I
don't know how to; it's not always plausible/possible. At most I will
use a finally block to "clean up". However, clean up always takes
place, so I can't put an "undo" operation here. I feel stupid dealing
with clean up in each catch or even one catch. Your time-traveling Dog
ends up looking like this:

public void CelebrateBirthd ay()
{
int ageChange = new Random().Next(1 0); // what does -1 mean for a
Dog that can travel to the past?
try
{
age += ageChange;
}
catch
{
age -= ageChange;
}
}

My version would be about three lines long. I don't need to worry
about the scope of ageChange; I don't need to handle all the other
variables that go into my time-travelling Dog. True, I only need to
store the variables I change, but how do I know one of the methods
inside my method won't change someday to alter additional ones? Other
programmers aren't going to understand why you have all the additional
lines of code, just to add a few years to a poor dog's life. Where we
create an implementation method, the code is straight forward because
it removes all exception handling. It just does what it says it does
and nothing else. Since we can formalize the copy/modify/replace in
every public method, my programmers only need to remember one thing
and not need to understand (they can practically forget, like most
programmers do anyway) about exception safety. If efficiency is
absolutely critical, your method is pure adrenaline. But in general,
this approach is more readable and more maintainable, and hardly less
efficient.

Let me know if I am getting on your nerves. I think I have something
that is not just occassionally appropriate; I think it is appropriate
(not always the most efficient) for just about any exception-safe
class. I think efficiency gains should be made after a profiler has
said you should. It all comes down to a memcopy my friend; it just a
matter of how big the loop is.

~Travis
Jun 29 '07 #3

"Peter Duniho" <Np*********@nn owslpianmk.comw rote in message
news:op******** *******@petes-computer.local. ..
On Thu, 28 Jun 2007 18:54:44 -0700, je**********@gm ail.com
<je**********@g mail.comwrote:
>[...]
Notice that there is no longer a need for try/catch. If something goes
wrong, the copy, tempDog, just gets wasted. The final line is to
update 'this' with the changes.

Now, this seems really simple to me. I have trouble believing that
someone didn't come up with something more general or correct (I think
mine is correct???). For instance, there is a lot to be concerned
about with deep/shallow copies.

I'm afraid I don't really understand the question. The technique -- to
create a temporary instance on which one operates and then somehow replace
the original with the temporary version -- you describe is both general
and widespread. It applies in a number of scenarios, and not just in OOP
classes (for example, writing to a temp file, and only deleting the
original and renaming the temp file if the temp file is successfully and
completely created).

That said, C++ does not inherently support this technique any more than C#
does. So I don't really understand the comparison you seem to be making
Sure it does, with compiler-generated default operator=.
between C++ and C#.

As far as whether the technique is appropriate generally, I'd say much of
the time it's not. It really depends on what you're doing, but creating a
complete copy of an instance, modifying, and then copying all of that back
to the original has a lot of overhead and itself requires implementation
of the Copy() method or something like it. I have yet to run into a
situation where I felt it was unreasonable to simply do whatever cleanup I
need to do locally should an exception occur.

Note also that in your example, you sort of gloss over the fact that the
exception still needs to be caught somewhere. Yes, you can catch it and
The only place it needs to be caught, is at a layer of code that can *fix*
it (Tell the user to save somewhere else, retry the upload, etc). Any
low-level code is just going to have to rethrow it, better to not catch it
(and mess up the stack trace) in the first place. And yes, even a rethrow
messes up the stack trace.
just ignore it. But if you're going to have to write the exception
handler anyway, why not have it do something useful, like clean up after
whatever operation the code contained within was doing?

For those times when the technique is appropriate, then sure...I'd agree
that it's, well...appropri ate. Would I write it into every one of my
classes as a standard matter? Nope.
Hmm, C++ programmers do provide support nearly always. They either accept
the compiler-generated operator=, write their own, or explicitly disable
(and this is rare) assignment.
>
Pete
Jun 29 '07 #4
On Fri, 29 Jun 2007 00:21:29 -0700, Ben Voigt [C++ MVP]
<rb*@nospam.nos pamwrote:
[...]
>That said, C++ does not inherently support this technique any more than
C# does. So I don't really understand the comparison you seem to be
making

Sure it does, with compiler-generated default operator=.
That's not my point. You still need to write the code that _uses_ that,
and because the default operator= does a shallow copy, it only works for
the simplest of classes. You can easily accomplish the same thing in C#
with a few extra lines of code in the class, but neither language provides
a universal solution.
[...]
The only place it needs to be caught, is at a layer of code that can
*fix* it (Tell the user to save somewhere else, retry the upload, etc)..
Any low-level code is just going to have to rethrow it, better to not
catch it (and mess up the stack trace) in the first place. And yes,
even a rethrow messes up the stack trace.
I haven't ever found the behavior related to rethrowing exceptions to
interfere with debugging a problem. Maybe that's because when I rethrow
exceptions, I generally reference the current exception as the
InnerException of the new exception. In any case, I would prefer to
sacrifice some efficiency in the exceptional case, rather than incur a
loss in efficiency in all cases.

A matter of preference, I realize. But don't discount it out of hand just
because you don't happen to agree.
[...]
Hmm, C++ programmers do provide support nearly always. They either
accept the compiler-generated operator=, write their own, or explicitly
disable (and this is rare) assignment.
I have no idea what you mean by this? Are you claiming to know what the
general population of all C++ programmers do as a matter of habit?

I've seen a LOT of C++ code in my day, and very little of it uses the
operator= with a C++ class at all, never mind accepts the
compiler-generated operation, implements their own, or explicitly disables
it.

Your experience may be different, but it seems to me you should be careful
about drawing broad generalizations based solely on your own personal
experience.

Pete
Jun 29 '07 #5
On Thu, 28 Jun 2007 21:42:00 -0700, je**********@gm ail.com
<je**********@g mail.comwrote:
[...]
Let me know if I am getting on your nerves. I think I have something
that is not just occassionally appropriate; I think it is appropriate
(not always the most efficient) for just about any exception-safe
class.
You're not getting on my nerves. I'm just offering my own viewpoint. I
thought that's what you were asking for.

Just because I happen to disagree with you, that shouldn't make you think
you're getting on my nerves. It would be pretty stupid to be annoyed at
someone just because they disagreed with you. Wouldn't it?

Pete
Jun 29 '07 #6
I think an assumption is being made that one can come up with a standard way
of handling unexpected conditions. I don't believe this is possible. The
copy-update-swap method could still generate an exception during the swap,
after all.

If an exception is considered "possible" (like not having enough disk space
to write a file), then it should be wrapped in a try-catch and retried
appropriately. But most exceptions are not considered "possible." Many of
them result from bugs, for example. So who knows what the app should do if
it does catch an exception it didn't think was possible? Not much, IMO.

I think the only realistic way to handle exceptions in a general way is
simply to throw away the work that's been done and start over (which may
involve restarting the application). If database changes are occurring, then
the "throwing away" part requires transactions at the code level, which
aren't committed when there's an exception.

These are just some general thoughts about a general question. As always, it
helps to provide a concrete, realistic example when proposing a pattern. In
the case of the dog's birthday, even if the dog was rolled back, what should
the rest of the app do to recover from the exception? If it's just to print
a message and exit, who cares about the dog?

///ark
Jun 29 '07 #7
<je**********@g mail.comwrote in message
news:11******** **************@ q69g2000hsb.goo glegroups.com.. .
try
{
age += ageChange;
}
catch
{
age -= ageChange;
}
}
Presumably, there would be additional code in the try-block, possibly both
before and after the assignment. How would the catch-block know what to roll
back, without introducing flags, or a try-catch around each statement, or
something equally unwieldy?

If the app experiences a totally unexpected exception, I think restoration
of the dog's state is the least of its worries.

///ark
Jun 29 '07 #8
On Jun 29, 10:40 am, "Peter Duniho" <NpOeStPe...@nn owslpianmk.com>
wrote:
On Thu, 28 Jun 2007 21:42:00 -0700, jehugalea...@gm ail.com

<jehugalea...@g mail.comwrote:
[...]
Let me know if I am getting on your nerves. I think I have something
that is not just occassionally appropriate; I think it is appropriate
(not always the most efficient) for just about any exception-safe
class.

You're not getting on my nerves. I'm just offering my own viewpoint. I
thought that's what you were asking for.

Just because I happen to disagree with you, that shouldn't make you think
you're getting on my nerves. It would be pretty stupid to be annoyed at
someone just because they disagreed with you. Wouldn't it?

Pete
Well, I didn't want you to think I was trying to contradict you or
anything.

~Travis

Jun 29 '07 #9
On Jun 29, 12:15 pm, "Mark Wilden" <mwil...@commun itymtm.comwrote :
I think an assumption is being made that one can come up with a standard way
of handling unexpected conditions. I don't believe this is possible. The
copy-update-swap method could still generate an exception during the swap,
after all.
I don't think there is a "standard" way of handling exceptions; this
post proves it. This is just something my group can agree to do for
our classes, if we need to or we don't want to deal with exceptions in
some other manner. There is something very important about swap; it
must be exception safe. I think that if we are careful, we can make
all swaps nothing but assignments to existing objects, which would be
exception safe. As long as you don't try to construct anything on the
heap, you should be fine. Since your copy is created prior to any code
executing, you know it is stable before we begin doing any work.
If an exception is considered "possible" (like not having enough disk space
to write a file), then it should be wrapped in a try-catch and retried
appropriately. But most exceptions are not considered "possible." Many of
them result from bugs, for example. So who knows what the app should do if
it does catch an exception it didn't think was possible? Not much, IMO.
I would like to get away from exception handling. I think it is its
own subject. You are a bit of a fatalist when it comes to exception
handling. I agree that many bugs are hard to deal with or should not
be dealt with at all. Personally, I put up walls in my code to handle
exceptions at certain levels when I want to protect higher layers.
However, every application requires something somewhat different.

I think the only realistic way to handle exceptions in a general way is
simply to throw away the work that's been done and start over (which may
involve restarting the application). If database changes are occurring, then
the "throwing away" part requires transactions at the code level, which
aren't committed when there's an exception.
I am trying to "start over". My Dog could have been around for a long
time before a method gets called. My Dog is pretty popular, getting
put in lots of collections. He has a lot of references out there. I
may even have different layers of code touching this pup. It might not
be possible to just back up and start over, if, say, my Dog is the top
most class in my application. I am localizing the changes to a small
section of code. I think it is, well, cute the way I do it.

Say that we don't know exactly how much a class is going to be used.
One day we may agree that the Dog should be put down if he fails. The
next day we decide that every Dog should have their day, so we give
them more responsibility. If we didn't want out poor Dog getting wiped
out during an error, we will need to change how he faces exceptions.
Why not write my code the first time through, and never need to worry
about where code will go.
These are just some general thoughts about a general question. As always, it
helps to provide a concrete, realistic example when proposing a pattern. In
the case of the dog's birthday, even if the dog was rolled back, what should
the rest of the app do to recover from the exception? If it's just to print
a message and exit, who cares about the dog?

///ark

Jun 29 '07 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

5
5338
by: John Harrison | last post by:
Where can I find information on exception safety in the STL? I.e. which methods on which types offer what level of exception safety. Josuttis has a useful list of classes and methods but he fails to mention common operations such as copy construction and assignment. I can't find any mention of this in my copy of the standard at all, although...
10
30264
by: Gary.Hu | last post by:
I was trying to catch the Arithmetic exception, unsuccessfully. try{ int a = 0, b = 9; b = b / a; }catch(...){ cout << "arithmetic exception was catched!" << endl; } After ran the program, it quitted with core dumped. %test
4
2014
by: robinsand | last post by:
Header File: car.h #if !defined CAR_H #define CAR_H enum TCarType { ctEconomy = 1, ctCompact, ctStandard, ctFullSize, ctMiniVan, ctSUV }; class Car { public: Car();
7
5097
by: Sek | last post by:
Hi Folks! I was pondering over a code and noticed that exception handlers were present in the private, protected as well as public methods. And, ofcourse, public methods were calling priv/prot methods internally. My thought was, the exception is being rethrown and propagated by the non-public methods to the public methods, causing...
132
5470
by: Zorro | last post by:
The simplicity of stack unraveling of C++ is not without defective consequences. The following article points to C++ examples showing the defects. An engineer aware of defects can avoid hard-to-find bugs. http://distributed-software.blogspot.com/2007/01/c-exception-handling-is-defective.html Regards, zorabi@ZHMicro.com...
3
3115
by: Aarti | last post by:
I was reading about exception safety in vectors and came across a staement that says The clear operation of vector guarantees a nothrow provided the copy and assignment operator do not throw an exception. Now clear just deletes all the elements of a vector. What has copy and assignment operator got to do with it? Can anyone please shed...
11
2096
by: George2 | last post by:
Hello everyone, How do you understand the Bjarne's comments about exception specification? Especially, "not required to be checked across compilation-unit" and "violations will not be caught at run time"? section 14.6.1 Checking Exception Specifications --------------------
12
2738
by: Ioannis Vranos | last post by:
Perhaps a mechanism can be introduced in the C++0x/1x standard, something simple like defining a function as: void somefunc(void) throw() { // ... }
1
1437
by: Vincent Jacques | last post by:
Hello all, I'm writing a class with value semantic, and I want its public interface to provide the strong exception safety guaranty (if a public operation fails for any reason, then an exception is thrown, and the object is left in its previous state) My class's data members are standard containers and built-in types. For
0
7464
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main...
0
7805
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that...
0
7751
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the...
0
5968
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then...
1
5323
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes...
0
4943
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert...
0
3440
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
1874
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
1
1012
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.