Use of unassigned local variable on finally block - c#

When could i in this example be unassigned?
int i;
try
{
i = 2;
}
catch
{
i = 3;
}
finally
{
string a = i.ToString();
}

You could get a ThreadAbortException before i=2 runs, for example. Anyway, the C# compiler is not exceptionally smart, so it's easy to fool with contrived examples like the above one. It doesn't have to recognize every situation, and if it's not sure it is assigned, even if you are sure, it will complain.
EDIT: I was a bit quick on my first assumption. So to refine it, here's what I think. Code is guaranteed to run in order, or if an exception happens, it will jump to the handlers. So i=2 may not run if an exception happens before that. I still claim that a ThreadAbortException is one of the few reasons why this can happen, even you have no code which could produce exceptions. Generally, if you have any number of different exception handlers, the compiler cannot know in advance which one will run. So it doesn't try to make any assumptions about that. It could know that if 1) there is only 1 catch block and 2) it is typeless, then, and only then, that one catch block is guaranteed to run. Or, if there were multiple catch handlers, and you assigned your variable in every one of them, it could also work, but I guess the compiler doesn't care about that either. However simple it may seem, it is a special case, and the C# compiler team has a tendency to ignore those special cases.

It's highly unlikely it will happen with the example you have posted. However, the compliler is going to be 'helpful' in this situation. As Hadas said, just initialize i to 0.

There are lots of code samples that one can write in which it is provably the case that a variable is assigned but that the compiler simply cannot prove that it is definitely assigned.
Just consider this much simpler case:
int i;
if ((bool)(object)true)
i = 0;
Console.WriteLine(i);
It is provably impossible for that case to ever access an unassigned i as well, yet it won't compile.
It is also provably impossible for the compiler to solve this problem in the general case. There are cases where it can prove a variable is certainly not definitely assigned, and there are cases where it can prove it definitely is assigned, but there are also cases where it just doesn't know either way. In those cases it chooses to fail, because it sees a few false positive errors as less harmful than false negatives.
To speak more about your specific case; you're saying that if a variable is assigned in both the try and catch blocks it is definitely assigned. While that may be true of your specific code, it's certainly not true in the general case. You need to consider exceptions that aren't handled by the catch block (even in your case, where none is specified, exceptions such as a stack overflow or out of memory won't be caught), you need to consider the catch block itself throwing an exception (again, it won't happen in your case, but the compiler would need to prove that to compile the code).

I think it would help if you to look at another type besides a primitive. Consider:
int i;
MyClass ed = new MyClass();
try
{
int newI = ed.getIntFromFunctionThatWillThrow();
i = newI;
}
catch (Exception e)
{
i = 3;
// do some abortion code.
}
finally
{
string a = i.ToString();
...
}
So in this block of code, there is no lexicographical guarantee for any one branch of execution. You have two (at least) branches to consider: The try-finally branch, and the try-catch branch. Since the function "getIntFromFunctionThatWillThrow" is going to throw (See what I did there?) i will be left unassigned, even though I try to use it later. However, that isn't something that is recognized until after runtime, like I have no idea what section of code this will get into if I have no inside information about the member on ed. So the compiler has no idea what value i will be. It only knows that it exists, and is of type int.
If it was an issue, a fix is to set i an initial value, this will suppress the error.
I hope this helps somewhat!

Related

c#: warning if results discarded

I've had several timewasting bugs from my mistakes where the result of an expression is not used, so is silently discarded, something like this
x.something(...)
instead of the intended
var y = x.something(...)
This is trivial to catch and I want the compiler to do so. If I really want to throw away the result I'd be happy to cast to void or use a discard (_ = something()). Is there any way of it catching such silly mistakes? If so, are there other checks I can enable as well that may prove useful?

Compile time constants and reference types

Ok, consider the following code:
const bool trueOrFalse = false;
const object constObject = null;
void Foo()
{
if (trueOrFalse)
{
int hash = constObject.GetHashCode();
}
if (constObject != null)
{
int hash = constObject.GetHashCode();
}
}
trueOrFalse is a compile time constant and as such, the compiler warns correctly that int hash = o.GetHashCode(); is not reachable.
Also, constObject is a compile time constant and as such the compiler warns again correctly that int hash = o.GetHashCode(); is not reachable as o != null will never be true.
So why doesn't the compiler figure out that:
if (true)
{
int hash = constObject.GetHashCode();
}
is 100% sure to be a runtime exception and thus issue out a compile time error? I know this is probably a stupid corner case, but the compiler seems pretty smart reasoning about compile time constant value types, and as such, I was expecting it could also figure out this small corner case with reference types.
UPDATE: This question was the subject of my blog on July 17th 2012. Thanks for the great question!
Why doesn't the compiler figure out that my code is 100% sure to be a runtime exception and thus issue out a compile time error?
Why should the compiler make code that is guaranteed to throw into a compile-time error? Wouldn't that make:
int M()
{
throw new NotImplementedException();
}
into a compile-time error? But that's exactly the opposite of what you want it to be; you want this to be a runtime error so that the incomplete code compiles.
Now, you might say, well, dereferencing null is clearly undesirable always, whereas a "not implemented" exception is clearly desirable. So could the compiler detect just this specific situation of there being a null ref exception guaranteed to happen, and give an error?
Sure, it could. We'd just have to spend the budget on implementing a data flow analyzer that tracks when a given expression is known to be always null, and then make it a compile time error (or warning) to dereference that expression.
The questions to answer then are:
How much does that feature cost?
How much benefit does the user accrue?
Is there any other possible feature that has a better cost-to-benefit ratio, and provides more value to the user?
The answer to the first question is "rather a lot" -- code flow analyzers are expensive to design and build. The answer to the second question is "not very much" -- the number of situations in which you can prove that null is going to be dereferenced are very small. The answer to the third question has, over the last twelve years, always been "yes".
Therefore, no such feature.
Now, you might say, well, C# does have some limited ability to detect when an expression is always/never null; the nullable arithmetic analyzer uses this analysis to generate more optimal nullable arithmetic code (*), and clearly the flow analyzer uses it to determine reachability. So why not just use the already existing nullability and flow analyzer to detect when you've always dereferenced a null constant?
That would be cheap to implement, sure. But the corresponding user benefit is now tiny. How many times in real code do you initialize a constant to null, and then dereference it? It seems unlikely that anyone would actually do that.
Moreover: yes, it is always better to detect a bug at compile time instead of run time, because it is cheaper. But the bug here -- a guaranteed dereference of null -- will be caught the first time the code is tested, and subsequently fixed.
So basically the feature request here is to detect at compile time a very unlikely and obvioulsy wrong situation that will always be immediately caught and fixed the first time the code is run anyways. It is therefore not a very good candidate for spending budget on to implement it; we have lots of higher priorities.
(*) See the long series of articles on how the Roslyn compiler does so which begins at http://ericlippert.com/2012/12/20/nullable-micro-optimizations-part-one/
While unreachable code is useless and does not affect your execution, code that throws an error is executed. So
if (true) { int hash = constObject.GetHashCode();}
is more or less the same as
throw new NullReferenceException();
You might very well want to throw that null reference. Whereas the unreachable code is just taking up space if it were to be compiled.
It also won't warn about the following code that has the same effect:
throw new NullReferenceException();
There's a balance with warnings. Most compiler errors happen when the compiler can't produce anything meaningful from it.
Some happen with things that affect verifiability, or which cross a threshold of how likely they are to be a bug. For example, the following code:
private void DoNothing(out string str)
{
return;
}
private void UseNothing()
{
string s;
DoNothing(s);
}
Won't compile, though if it did it would do no harm (the only place DoNothing is called doesn't use the string passed, so the fact that it is never assigned isn't a problem). There's just too high a risk that I'm doing something stupid here to let it go.
Warnings are for things that are almost certainly foolish or at least not what you wanted to happen. Dead code is likely enough to be a bug to make a warning worthwhile, but likely enough to be sensible (e.g. trueOrFalse may change as the application is developed) to make an error inappropriate.
Warnings are meant to be useful, rather than nuisances, so the bar for them is put quite high. There's no exact science, but it was deemed that unreachable code made the cut, and trying to deduce when throwing exceptions wasn't the desired behaviour didn't.
It no doubt helps that the compiler already detects unreachable code (and doesn't compile it) but sees one deliberate throw much like another, no matter how convoluted on the one hand or direct on the other.
Why would you even want that to be a compile-time error? Why would you want code that is guaranteed to throw an exception to be invalid at compile time? What if I, the programmer, want the semantics of my program to be:
static void Main(string[] args) {
throw new NullReferenceException();
}
It's my program. The compiler has no business telling me this isn't valid C# code.

Why does Try-Catch require curly braces

Just curious: Why is the syntax for try catch in C# (Java also?) hard coded for multiple statements? Why doesn't the language allow:
int i;
string s = DateTime.Now.Seconds % 2 == 1 ? "1" : "not 1";
try
i = int.Parse(s);
catch
i = 0;
The example is for trivial purposes only. I know there's int.TryParse.
Consider the fact that there are really three (or more) code blocks in play here:
try {}
catch (myexcption)
{}
catch (myotherexception)
{}
finally
{}
Keep in mind that these are in the scope of a larger context and the exceptions not caught are potentually caught further up the stack.
Note that this is basically the same thing as a class construct that also has the {} structure.
Say for instance you might have:
try
try
if (iAmnotsane)
beatMe(please);
catch (Exception myexception)
catch (myotherexception)
logerror("howdy")
finally
NOW does that second catch belong to the first or the second try? What about the finally? SO you see the optional/multiple portions make the requirement.
UPDATE: This question was the subject of my blog on December 4th, 2012. There are a number of insightful comments on the blog that you might also be interested in. Thanks for the great question!
As others have noted, the proposed feature introduces ambiguities that are confusing. I was interested to see if there were any other justifications for the decision to not support the feature, so I checked the language design notes archive.
I see nothing in the language design notes archive that justifies this decision. As far as I know, C# does it that way because that's how other languages with similar syntax do it, and they do it that way because of the ambiguity problem.
I did learn something interesting though. In the initial design of C# there was no try-catch-finally! If you wanted a try with a catch and a finally then you had to write:
try
{
try
{
XYZ();
}
catch(whatever)
{
DEF();
}
}
finally
{
ABC();
}
which, not surprisingly, is exactly how the compiler analyzes try-catch-finally; it just breaks it up into try-catch inside try-finally upon initial analysis and pretends that's what you said in the first place.
More or less, this is a play on the dangling else problem.
For example,
if( blah )
if ( more blah )
// do some blah
else
// no blah I suppose
Without curly braces, the else is ambiguous because you don't know if it's associated with the first or second if statement. So you have to fallback on a compiler convention (e.g. in Pascal or C, the compiler assumes the dangling else is associated with the closest if statement) to resolve the ambiguity, or fail the compile entirely if you don't want to allow such ambiguity in the first place.
Similarly,
try
try
// some code that throws!
catch(some blah)
// which try block are we catching???
catch(more blah )
// not so sure...
finally
// totally unclear what try this is associated with.
You could solve it with a convention, where catch blocks are always associated with the closest try, but I find this solution generally allows programmers to write code that is potentially dangerous. For example, in C, this:
if( blah )
if( more blah )
x = blah;
else
x = blahblah;
...is how the compiler would interpret this if/if/else block. However, it's also perfectly legitimate to screw up your indenting and write:
if( blah )
if( more blah )
x = blah;
else
x = blahblah;
...which now makes it appear like the else is associated with the outer if statement, when in fact it is associated with the inner if statement due to C conventions. So I think requiring the braces goes a long way towards resolving ambiguity and preventing a rather sneaky bug (these sorts of issues can be trivial to miss, even during code inspection). Languages like python don't have this issue since indentation and whitespace matter.
If you assume that the designers of C# simply choose to use the same syntax as C++ then the question becomes why are braces necessary with single statements try and catch blocks in C++. The simple answer is that Bjarne Stroustrup thought the syntax was easier to explain.
In The Design and Evolution of C++ Stroustrup writes:
"The try keyword is completely redundant and so are the { } braces except where multiple statements are actually used in a try-block or a handler."
He goes on to give an example where the try keyword and { } are not needed. He then writes:
"However, I found this so difficult to explain that the redundancy was introduced to save support staff from confused users."
Reference:
Stroustrup, Bjarne (1994). The Design and Evolution of C++. Addison-Wesley.
The first think I can think of is that the curly braces create a block with its own variable scope.
Look at the following code
try
{
int foo = 2;
}
catch (Exception)
{
Console.WriteLine(foo); // The name 'foo' does not exist in the current context
}
foo is not accessible in the catch block due to the variable scoping. I think this makes it easier to reason about whether an variable has been initialized before use or not.
Compare with this code
int foo;
try
{
foo = 2;
}
catch (Exception)
{
Console.WriteLine(foo); // Use of unassigned local variable 'foo'
}
here you can not guarantee that foo is initialized.
try // 1
try // 2
something();
catch { // A
}
catch { // B
}
catch { // C
}
does B catches try 1 or 2?
I don't think you can resolve this unambiguously, since the snippet might mean:
try // 1
{
try // 2
something();
catch { // A
}
}
catch { // B
}
catch { // C
}
try // 1
{
try // 2
something();
catch { // A
}
catch { // B
}
}
catch { // C
}
Probably to discourage overuse. A try-catch block is big and ugly, and you're going to notice when you're using it. This mirrors the effect that a catch has on your application's performance - catching an exception is extremely slow compared to a simple boolean test.
In general you should avoid errors, not handle them. In the example you give, a much more efficient method would be to use
if(!int.TryParse(s, out i))
i=0;
The rational is that it's more maintainable (easier to change, less likely to break, ergo higher quality):
it's clearer, and
it's easier to change because if you need to add a line to your blocks you don't introduce a bug.
As to why exception handling is different than conditional expressions...
If/Else is conditional upon an expression to use one of two (or more If/Else if/Else) paths in the code
Try/Catch is part of exception handling, it is not a conditional expression. Try/Catch/Finally operates only when an exception has been thrown inside the scope of the Try block.
Exception handling will traverse up the stack/scope until it finds a Catch block that will catch the type of exception that was thrown. Forcing scope identifiers makes this check for blocks simplified. Forcing you to scope when dealing with exceptions seems like a good idea, it also is a good indication that this is part of exception handling rather than normal code. Exceptions are exceptions, not something you really want happening normally but know can happen and want to handle when they do happen.
EDIT: There is one more reason which I can think of, is that CATCH is mandatory after a TRY unlike ELSE. Hence there needs to be definite way to define the TRY block.
Another way of looking at this…
Given all the maintenance problem that have been created by “if”, “while”, “for” and “foreach” statements without bases, a lot of companies have coding standards that always require bases on statements that act on a “block”.
So they make you write:
if (itIsSo)
{
ASingleLineOfCode();
}
Rather then:
if (itIsSo)
ASingleLineOfCode();
(Note as indenting is not checked by the compiler, it can't be depended on to be right)
A good case could be made for designing a language that always require the bases, but then too many people would have hated C# due to having to always use the bases. However for try/catch there was not an expectation of being able to get away without using the bases, so it was possible to require them without to many people complaining.
Given a choose I would much rather have if/endIf (and while/endWhile) as the block delimiters but the USA got its way on that one. (C got to define what most languages look like rather than Module2, afterall most of what we do is defined by history not logic)
The simplest (I think) answer is that each block of code in C/C++/C# requires curly braces.
EDIT #1
In response to negative votes, directly from MSDN:
try-catch (C# Reference)
The try-catch statement consists of a try block followed by one or more catch clauses, which specify handlers for different exceptions.
As per definition says, it is a block, so it requires curly braces. That is why we cannot use it without { }.

Does C# have a way to mimic Software Transactional Memory, on a small scale?

Does C# have a way to temporarily change the value of a variable in a specific scope and revert it back automatically at the end of the scope/block?
For instance (not real code):
bool UpdateOnInput = true;
using (UpdateOnInput = false)
{
//Doing my changes without notifying anyone
Console.WriteLine (UpdateOnInput) // prints false
}
//UpdateOnInput is true again.
EDIT:
The reason I want the above is because I don't want to do this:
UpdateOnInput = false
//Doing my changes without notifying anyone
Console.WriteLine (UpdateOnInput) // prints false
UpdateOnInput = true
No, there's no way to do this directly. There are a few different schools of thought on how to do this sort of thing. Compare and contrast these two:
originalState = GetState();
SetState(newState);
DoSomething();
SetState(originalState);
vs
originalState = GetState();
SetState(newState);
try
{
DoSomething();
}
finally
{
SetState(originalState);
}
Many people will tell you that the latter is "safer".
It ain't necessarily so.
The difference between the two is of course the the latter restores the state even if DoSomething() throws an exception. Is that better than keeping the state mutated in an exception scenario? What makes it better? You have an unexpected, unhandled exception reporting that something awful and unexpected has happened. Your internal state could be completely inconsistent and arbitrarily messed up; no one knows what might have been happening at the point of the exception. All we know is that DoSomething probably was trying to do something to the mutated state.
Is it really the right thing to do in the scenario where something terrible and unknown has happened to keep on stirring that particular pot and trying to mutate the state that just caused an exception again?
Sometimes that is going to be the right thing to do, and sometimes its going to make matters worse. Which scenario you're actually in depends on what exactly the code is doing, so think carefully about what the right thing to do is before just blindly choosing one or the other.
Frankly, I would rather solve the problem by not getting into the situation in the first place. Our existing compiler design uses this design pattern, and frankly, it is freakin' irritating. In the existing C# compiler the error reporting mechanism is "side effecting". That is, when part of the compiler gets an error, it calls the error reporting mechanism which then displays the error to the user.
This is a major problem for lambda binding. If you have:
void M(Func<int, int> f) {}
void M(Func<string, int> f) {}
...
M(x=>x.Length);
then the way this works is we try to bind
M((int x)=>{return x.Length;});
and
M((string x)=>{return x.Length;});
and we see which one, if any, gives us an error. In this case, the former gives an error, the latter compiles without error, so this is a legal lambda conversion and overload resolution succeeds. What do we do with the error? We cannot report it to the user because this program is error free!
Therefore what we do when we bind the body of a lambda is exactly what you say: we tell the error reporter "don't report your errors to the user; save them in this buffer over here instead". Then we bind the lambda, restore the error reporter to its earlier state, and look at the contents of the error buffer.
We could avoid this problem entirely by changing the expression analyzer so that it returned the errors along with the result, rather than making errors a state-related side effect. Then the need for mutation of the error reporting state goes away entirely and we don't even have to worry about it.
So I would encourage you to revisit your design. Is there a way you can make the operation you are performing not dependent upon the state you are mutating? If so, then do that, and then you don't need to worry about how to restore the mutated state.
(And of course in our case we do want to restore the state upon an exception. If something inside the compiler throws during lambda binding, we want to be able to report that to the user! We don't want the error reporter to stay in the "suppress reporting errors" state.)
No, but it is pretty simple to just do this:
bool UpdateOnTrue = true;
// ....
bool temp = UpdateOnTrue;
try
{
UpdateOnTrue = false;
// do stuff
}
finally
{
UpdateOnTrue = temp;
}
Try:
public void WithAssignment<T>(ref T var, T val, Action action)
{
T original = var;
var = val;
try
{
action();
}
finally
{
var = original;
}
}
Now you can say:
bool flag = false;
WithAssignment(ref flag, true, () =>
{
// flag is true during this block
});
// flag is false again
No, you have to do it manually with a try/finally block. I dare say you could write an IDisposable implementation which would do something hacky in conjunction with lambda expressions, but I suspect a try/finally block is simpler (and doesn't abuse the using statement).
Sounds like you really want
Stack<bool>
No , there is not standard way, you should implement it manually. Generic implementation of IEditableObject via TypeDescriptor and Reflection can be helpfull
Canned... I doubt it. Given your example is a simple use of a temporary bool value I'm assuming you've got something wacky in mind :-) You can implement some kind of Stack stucture:
1) Push old value onto stack
2) Load new value
3) Do Stuff
4) Pop from stack and replace used value.
Rough (AKA Untested) example (Can't look up stack syntax right now)
bool CurrentValue = true;
Stack<bool> Storage= new Stack<bool>
Storage.Push(CurrentValue);
CurrentValue=false;
DoStuff();
CurrentValue = Storage.Pop();
//Continue
You should refactor your code to use a separate function, like so:
bool b = GetSomeValue();
DoSomething(ModifyValue(b));
//b still has the original value.
For this to work for a reference type, you need to copy it before messing with it:
ICloneable obj = GetSomeValue();
DoSomething(ModifyValue(obj.Clone()));
//obj still has the original value.
It's hard to write correct code when the values of your variables change around a lot. Strive to have as few reassignments in your code as possible.

What is the real overhead of try/catch in C#?

So, I know that try/catch does add some overhead and therefore isn't a good way of controlling process flow, but where does this overhead come from and what is its actual impact?
Three points to make here:
Firstly, there is little or NO performance penalty in actually having try-catch blocks in your code. This should not be a consideration when trying to avoid having them in your application. The performance hit only comes into play when an exception is thrown.
When an exception is thrown in addition to the stack unwinding operations etc that take place which others have mentioned you should be aware that a whole bunch of runtime/reflection related stuff happens in order to populate the members of the exception class such as the stack trace object and the various type members etc.
I believe that this is one of the reasons why the general advice if you are going to rethrow the exception is to just throw; rather than throw the exception again or construct a new one as in those cases all of that stack information is regathered whereas in the simple throw it is all preserved.
I'm not an expert in language implementations (so take this with a grain of salt), but I think one of the biggest costs is unwinding the stack and storing it for the stack trace. I suspect this happens only when the exception is thrown (but I don't know), and if so, this would be decently sized hidden cost every time an exception is thrown... so it's not like you are just jumping from one place in the code to another, there is a lot going on.
I don't think it's a problem as long as you are using exceptions for EXCEPTIONAL behavior (so not your typical, expected path through the program).
Are you asking about the overhead of using try/catch/finally when exceptions aren't thrown, or the overhead of using exceptions to control process flow? The latter is somewhat akin to using a stick of dynamite to light a toddler's birthday candle, and the associated overhead falls into the following areas:
You can expect additional cache misses due to the thrown exception accessing resident data not normally in the cache.
You can expect additional page faults due to the thrown exception accessing non-resident code and data not normally in your application's working set.
for example, throwing the exception will require the CLR to find the location of the finally and catch blocks based on the current IP and the return IP of every frame until the exception is handled plus the filter block.
additional construction cost and name resolution in order to create the frames for diagnostic purposes, including reading of metadata etc.
both of the above items typically access "cold" code and data, so hard page faults are probable if you have memory pressure at all:
the CLR tries to put code and data that is used infrequently far from data that is used frequently to improve locality, so this works against you because you're forcing the cold to be hot.
the cost of the hard page faults, if any, will dwarf everything else.
Typical catch situations are often deep, therefore the above effects would tend to be magnified (increasing the likelihood of page faults).
As for the actual impact of the cost, this can vary a lot depending on what else is going on in your code at the time. Jon Skeet has a good summary here, with some useful links. I tend to agree with his statement that if you get to the point where exceptions are significantly hurting your performance, you have problems in terms of your use of exceptions beyond just the performance.
Contrary to theories commonly accepted, try/catch can have significant performance implications, and that's whether an exception is thrown or not!
It disables some automatic optimisations (by design), and in some cases injects debugging code, as you can expect from a debugging aid. There will always be people who disagree with me on this point, but the language requires it and the disassembly shows it so those people are by dictionary definition delusional.
It can impact negatively upon maintenance. This is actually the most significant issue here, but since my last answer (which focused almost entirely on it) was deleted, I'll try to focus on the less significant issue (the micro-optimisation) as opposed to the more significant issue (the macro-optimisation).
The former has been covered in a couple of blog posts by Microsoft MVPs over the years, and I trust you could find them easily yet StackOverflow cares so much about content so I'll provide links to some of them as filler evidence:
Performance implications of try/catch/finally (and part two), by Peter Ritchie explores the optimisations which try/catch/finally disables (and I'll go further into this with quotes from the standard)
Performance Profiling Parse vs. TryParse vs. ConvertTo by Ian Huff states blatantly that "exception handling is very slow" and demonstrates this point by pitting Int.Parse and Int.TryParse against each other... To anyone who insists that TryParse uses try/catch behind the scenes, this ought to shed some light!
There's also this answer which shows the difference between disassembled code with- and without using try/catch.
It seems so obvious that there is an overhead which is blatantly observable in code generation, and that overhead even seems to be acknowledged by people who Microsoft value! Yet I am, repeating the internet...
Yes, there are dozens of extra MSIL instructions for one trivial line of code, and that doesn't even cover the disabled optimisations so technically it's a micro-optimisation.
I posted an answer years ago which got deleted as it focused on the productivity of programmers (the macro-optimisation).
This is unfortunate as no saving of a few nanoseconds here and there of CPU time is likely to make up for many accumulated hours of manual optimisation by humans. Which does your boss pay more for: an hour of your time, or an hour with the computer running? At what point do we pull the plug and admit that it's time to just buy a faster computer?
Clearly, we should be optimising our priorities, not just our code! In my last answer I drew upon the differences between two snippets of code.
Using try/catch:
int x;
try {
x = int.Parse("1234");
}
catch {
return;
}
// some more code here...
Not using try/catch:
int x;
if (int.TryParse("1234", out x) == false) {
return;
}
// some more code here
Consider from the perspective of a maintenance developer, which is more likely to waste your time, if not in profiling/optimisation (covered above) which likely wouldn't even be necessary if it weren't for the try/catch problem, then in scrolling through source code... One of those has four extra lines of boilerplate garbage!
As more and more fields are introduced into a class, all of this boilerplate garbage accumulates (both in source and disassembled code) well beyond reasonable levels. Four extra lines per field, and they're always the same lines... Were we not taught to avoid repeating ourselves? I suppose we could hide the try/catch behind some home-brewed abstraction, but... then we might as well just avoid exceptions (i.e. use Int.TryParse).
This isn't even a complex example; I've seen attempts at instantiating new classes in try/catch. Consider that all of the code inside of the constructor might then be disqualified from certain optimisations that would otherwise be automatically applied by the compiler. What better way to give rise to the theory that the compiler is slow, as opposed to the compiler is doing exactly what it's told to do?
Assuming an exception is thrown by said constructor, and some bug is triggered as a result, the poor maintenance developer then has to track it down. That might not be such an easy task, as unlike the spaghetti code of the goto nightmare, try/catch can cause messes in three dimensions, as it could move up the stack into not just other parts of the same method, but also other classes and methods, all of which will be observed by the maintenance developer, the hard way! Yet we are told that "goto is dangerous", heh!
At the end I mention, try/catch has its benefit which is, it's designed to disable optimisations! It is, if you will, a debugging aid! That's what it was designed for and it's what it should be used as...
I guess that's a positive point too. It can be used to disable optimizations that might otherwise cripple safe, sane message passing algorithms for multithreaded applications, and to catch possible race conditions ;) That's about the only scenario I can think of to use try/catch. Even that has alternatives.
What optimisations do try, catch and finally disable?
A.K.A
How are try, catch and finally useful as debugging aids?
they're write-barriers. This comes from the standard:
12.3.3.13 Try-catch statements
For a statement stmt of the form:
try try-block
catch ( ... ) catch-block-1
...
catch ( ... ) catch-block-n
The definite assignment state of v at the beginning of try-block is the same as the definite assignment state of v at the beginning of stmt.
The definite assignment state of v at the beginning of catch-block-i (for any i) is the same as the definite assignment state of v at the beginning of stmt.
The definite assignment state of v at the end-point of stmt is definitely assigned if (and only if) v is definitely assigned at the end-point of try-block and every catch-block-i (for every i from 1 to n).
In other words, at the beginning of each try statement:
all assignments made to visible objects prior to entering the try statement must be complete, which requires a thread lock for a start, making it useful for debugging race conditions!
the compiler isn't allowed to:
eliminate unused variable assignments which have definitely been assigned to before the try statement
reorganise or coalesce any of it's inner-assignments (i.e. see my first link, if you haven't already done so).
hoist assignments over this barrier, to delay assignment to a variable which it knows won't be used until later (if at all) or to pre-emptively move later assignments forward to make other optimisations possible...
A similar story holds for each catch statement; suppose within your try statement (or a constructor or function it invokes, etc) you assign to that otherwise pointless variable (let's say, garbage=42;), the compiler can't eliminate that statement, no matter how irrelevant it is to the observable behaviour of the program. The assignment needs to have completed before the catch block is entered.
For what it's worth, finally tells a similarly degrading story:
12.3.3.14 Try-finally statements
For a try statement stmt of the form:
try try-block
finally finally-block
• The definite assignment state of v at the beginning of try-block is the same as the definite assignment state of v at the beginning of stmt.
• The definite assignment state of v at the beginning of finally-block is the same as the definite assignment state of v at the beginning of stmt.
• The definite assignment state of v at the end-point of stmt is definitely assigned if (and only if) either:
o v is definitely assigned at the end-point of try-block
o v is definitely assigned at the end-point of finally-block
If a control flow transfer (such as a goto statement) is made that begins within try-block, and ends outside of try-block, then v is also considered definitely assigned on that control flow transfer if v is definitely assigned at the end-point of finally-block. (This is not an only if—if v is definitely assigned for another reason on this control flow transfer, then it is still considered definitely assigned.)
12.3.3.15 Try-catch-finally statements
Definite assignment analysis for a try-catch-finally statement of the form:
try try-block
catch ( ... ) catch-block-1
...
catch ( ... ) catch-block-n
finally finally-block
is done as if the statement were a try-finally statement enclosing a try-catch statement:
try {
try
try-block
catch ( ... ) catch-block-1
...
catch ( ... ) catch-block-n
}
finally finally-block
In my experience the biggest overhead is in actually throwing an exception and handling it. I once worked on a project where code similar to the following was used to check if someone had a right to edit some object. This HasRight() method was used everywhere in the presentation layer, and was often called for 100s of objects.
bool HasRight(string rightName, DomainObject obj) {
try {
CheckRight(rightName, obj);
return true;
}
catch (Exception ex) {
return false;
}
}
void CheckRight(string rightName, DomainObject obj) {
if (!_user.Rights.Contains(rightName))
throw new Exception();
}
When the test database got fuller with test data, this lead to a very visible slowdown while openening new forms etc.
So I refactored it to the following, which - according to later quick 'n dirty measurements - is about 2 orders of magnitude faster:
bool HasRight(string rightName, DomainObject obj) {
return _user.Rights.Contains(rightName);
}
void CheckRight(string rightName, DomainObject obj) {
if (!HasRight(rightName, obj))
throw new Exception();
}
So in short, using exceptions in normal process flow is about two orders of magnitude slower then using similar process flow without exceptions.
Not to mention if it's inside a frequently-called method it may affect the overall behavior of the application.
For example, I consider the use of Int32.Parse as a bad practice in most cases since it throws exceptions for something that can be caught easily otherwise.
So to conclude everything written here:
1) Use try..catch blocks to catch unexpected errors - almost no performance penalty.
2) Don't use exceptions for excepted errors if you can avoid it.
I wrote an article about this a while back because there were a lot of people asking about this at the time. You can find it and the test code at http://www.blackwasp.co.uk/SpeedTestTryCatch.aspx.
The upshot is that there is a tiny amount of overhead for a try/catch block but so small that it should be ignored. However, if you are running try/catch blocks in loops that are executed millions of times, you may want to consider moving the block to outside of the loop if possible.
The key performance issue with try/catch blocks is when you actually catch an exception. This can add a noticeable delay to your application. Of course, when things are going wrong, most developers (and a lot of users) recognise the pause as an exception that is about to happen! The key here is not to use exception handling for normal operations. As the name suggests, they are exceptional and you should do everything you can to avoid them being thrown. You should not use them as part of the expected flow of a program that is functioning correctly.
I made a blog entry about this subject last year.
Check it out. Bottom line is that there is almost no cost for a try block if no exception occurs - and on my laptop, an exception was about 36μs. That might be less than you expected, but keep in mind that those results where on a shallow stack. Also, first exceptions are really slow.
It is vastly easier to write, debug, and maintain code that is free of compiler error messages, code-analysis warning messages, and routine accepted exceptions (particularly exceptions that are thrown in one place and accepted in another). Because it is easier, the code will on average be better written and less buggy.
To me, that programmer and quality overhead is the primary argument against using try-catch for process flow.
The computer overhead of exceptions is insignificant in comparison, and usually tiny in terms of the application's ability to meet real-world performance requirements.
I really like Hafthor's blog post, and to add my two cents to this discussion, I'd like to say that, it's always been easy for me to have the DATA LAYER throw only one type of exception (DataAccessException). This way my BUSINESS LAYER knows what exception to expect and catches it. Then depending on further business rules (i.e. if my business object participates in the workflow etc), I may throw a new exception (BusinessObjectException) or proceed without re/throwing.
I'd say don't hesitate to use try..catch whenever it is necessary and use it wisely!
For example, this method participates in a workflow...
Comments?
public bool DeleteGallery(int id)
{
try
{
using (var transaction = new DbTransactionManager())
{
try
{
transaction.BeginTransaction();
_galleryRepository.DeleteGallery(id, transaction);
_galleryRepository.DeletePictures(id, transaction);
FileManager.DeleteAll(id);
transaction.Commit();
}
catch (DataAccessException ex)
{
Logger.Log(ex);
transaction.Rollback();
throw new BusinessObjectException("Cannot delete gallery. Ensure business rules and try again.", ex);
}
}
}
catch (DbTransactionException ex)
{
Logger.Log(ex);
throw new BusinessObjectException("Cannot delete gallery.", ex);
}
return true;
}
We can read in Programming Languages Pragmatics by Michael L. Scott that the nowadays compilers do not add any overhead in common case, this means, when no exceptions occurs. So every work is made in compile time.
But when an exception is thrown in run-time, compiler needs to perform a binary search to find the correct exception and this will happen for every new throw that you made.
But exceptions are exceptions and this cost is perfectly acceptable. If you try to do Exception Handling without exceptions and use return error codes instead, probably you will need a if statement for every subroutine and this will incur in a really real time overhead. You know a if statement is converted to a few assembly instructions, that will performed every time you enter in your sub-routines.
Sorry about my English, hope that it helps you. This information is based on cited book, for more information refer to Chapter 8.5 Exception Handling.
Let us analyse one of the biggest possible costs of a try/catch block when used where it shouldn't need to be used:
int x;
try {
x = int.Parse("1234");
}
catch {
return;
}
// some more code here...
And here's the one without try/catch:
int x;
if (int.TryParse("1234", out x) == false) {
return;
}
// some more code here
Not counting the insignificant white-space, one might notice that these two equivelant pieces of code are almost exactly the same length in bytes. The latter contains 4 bytes less indentation. Is that a bad thing?
To add insult to injury, a student decides to loop while the input can be parsed as an int. The solution without try/catch might be something like:
while (int.TryParse(...))
{
...
}
But how does this look when using try/catch?
try {
for (;;)
{
x = int.Parse(...);
...
}
}
catch
{
...
}
Try/catch blocks are magical ways of wasting indentation, and we still don't even know the reason it failed! Imagine how the person doing debugging feels, when code continues to execute past a serious logical flaw, rather than halting with a nice obvious exception error. Try/catch blocks are a lazy man's data validation/sanitation.
One of the smaller costs is that try/catch blocks do indeed disable certain optimizations: http://msmvps.com/blogs/peterritchie/archive/2007/06/22/performance-implications-of-try-catch-finally.aspx. I guess that's a positive point too. It can be used to disable optimizations that might otherwise cripple safe, sane message passing algorithms for multithreaded applications, and to catch possible race conditions ;) That's about the only scenario I can think of to use try/catch. Even that has alternatives.

Categories