2

I have been writing in C# 4.0 a lot lately and trying to write as lean as possible. As such, I have not been using the classic try/catch blocks and using statements as often.

I understand the general function of .Net's garbage collection and exception handling - I want to bulletproof my code and be efficient with objects, but I am trying to achieve this with the minimum amount of 'extra code' (since 'bloat' pissed people off).

And to be clear, I understand using() is fundamental for relinquishing resources to handles and other code and its relationship to the IDisposable interface.

But I am trying to get a better grip about how assertive a programmer should be in exception handling objects.

Are there key places in code you'd say try/catch blocks and/or using statements are inarguably necessary? Within the contexts of garbage collection and exception handling, what common-use objects/scenarios would you recommend explicit attention?

Should you simply make a catch block for every possible exception an object might through? How deep should they be nested? Are there steps I can take to proactively collect garbage (for example, to combat the example in comments of waiting for Dispose() to be called)?

5
  • 1
    So, what do you use instead of try/catch and using? Commented Dec 13, 2011 at 19:14
  • @JohnSaunders If that's the best place for it, I understand. Thanks for your help. Commented Dec 13, 2011 at 19:21
  • 3
    I enjoyed Eric Lippert's post about vexing exceptions; he provides a good general categorization for exceptions with guidance on which ones should be caught and which ones shouldn't. If you think your code is bloated with try/catch, you could consider whether you are catching any exceptions that shouldn't be caught. Commented Dec 13, 2011 at 19:26
  • @AlbinSunnanbo I use both tools right now, just a lot less. Meaning, if there is an exception I truly want to handle, I'll use a try catch, but I haven't gone around stuffing everything possible in try/catch blocks. There are a couple objects just out of practice i put in using directives like working with Streams, but otherwise, I have not been handling IDisposable explicitly. I'm trying to learn explicit places it should be considered necessity, and when it can be safely overlooked for the sake of clean code. that clarify or confuse further? :) Commented Dec 13, 2011 at 19:30
  • @Dr.Wily'sApprentice That's exactly what I'm getting at... i guess im doing a poor job at phrasing my question because everyone keeps trying to tell me about how using is for IDisposable objects. duh. Commented Dec 13, 2011 at 20:02

6 Answers 6

9

using is not a "bloat", it's necessary to free resources wrapped into IDisposable objects. using is compiled into try...finally, with a call to the Dispose method in the finally section. For example, when StreamReader and StreamWriter are used to read and write files, respectively - their Dispose method automatically closes the file. Another example is SqlConnection - its Dispose method must be called to close the connection, and a using statement is a clean way to do it.

Regarding try...catch, their use in .NET does not differ from exception handling in C++ or Java. Exception handling decreases performance a bit, but that whether this is significant can only be determined by profiling the code. Exception handling must not be used for changing control flow, and it"s also not wise to use it to swallow exceptions. These and other important properties of exception handling can be found in OOP/C++/Java/... textbooks.

7
  • My understanding is that .Net's garbage collection will cleanup IDisposable stuff on its own, and that these statements are to help programmers be assertive and manually control when and how they want it done. If that's the case, I'm trying to figure out the places its most important for me to do the work, and the places its safe to let the .Net runtime be the whiz-kid it's supposed to be. Commented Dec 13, 2011 at 19:24
  • 5
    You have the wrong understanding. See my answer. IDisposable has nothing directly to do with garbage collection or memory. It is for cleaning up non-memory resources that also need to be cleaned up. Memory will take care of itself when an object goes out of scope, and trying to do it manually is not typically useful or necessary.
    – mquander
    Commented Dec 13, 2011 at 19:24
  • 2
    Please be careful: for example in case of SqlConnection, you must use a using statement, or call Dispose directly, to close the DB connection. The garbage collector won't do this for you: "If the SqlConnection goes out of scope, it won't be closed. Therefore, you must explicitly close the connection by calling Close or Dispose." --source: msdn.microsoft.com/en-us/library/…
    – kol
    Commented Dec 13, 2011 at 19:27
  • @Kol - thank you for your response. I know what the using statement means and how it ties into IDisposable. Your SqlConnection thought is more like what I'm getting at... where the garbage collector will not be enough. things that need manual attention. And in terms of try/catch, how assertive a programmer should be about catching possible exceptions simply because the object is capable of throwing them. Commented Dec 13, 2011 at 19:52
  • 1
    @one.beat You can easily tell which objects need manual disposing. They implement IDisposable. That means the API designer is telling you to dispose the object. Follow the instructions.
    – MarkJ
    Commented Dec 13, 2011 at 20:13
3

try/finally and using are almost totally orthogonal concerns to memory leaks. You should use using or try/finally when you have non-memory resources, like a lock, a handle, or a connection, and you need to make sure those are cleaned up.

There are very few or no common cases in .NET where you need to pay explicit attention to avoiding memory leaks. The only way you'll wind up with a memory leak if if you're retaining a reference to something old and you don't realize it; there are a few ways this can happen, like if that thing has an event you didn't unsubscribe from. But there's nothing mysterious.

7
  • Thanks for the reply. I guess not "leaks" per se, but objects no longer needed lingering in memory? Isn't the StreamWriter scenario a common one where without a using statement it could potentially linger around in memory until the garbage collector decides its ready for the trash can? Perhaps I need to relearn what I thought I knew. :( Commented Dec 13, 2011 at 19:28
  • 2
    No, definitely not! The problem is not that the StreamWriter is lingering in memory. The problem is (e.g. if you're writing to a file) that the StreamWriter, when opened, told Windows that it was taking control of a file, and never relinquished it when it was done, which means that other processes are probably locked out of writing to the same file. Calling Dispose in this case releases the file handle. Whether or not the StreamWriter is in memory hardly matters.*
    – mquander
    Commented Dec 13, 2011 at 19:30
  • * (Note: This is 95% of the truth. The other 5% is that when the garbage collector actually collects the object, the finalizer will run, and when the finalizer runs, it will call Dispose if you didn't already call Dispose yourself. However, since file handles and other similar things are important resources, it's bad to just wait and hope that the garbage collector will get around to it, so you should call Dispose yourself.)
    – mquander
    Commented Dec 13, 2011 at 19:32
  • Thanks Kor for clarifying a bit... its the 5% I'm getting at... and items the garbage collection cannot automatically dispose (SqlConnection, etc.). Those are still items in memory, no? As well as locked in resources. Commented Dec 13, 2011 at 19:53
  • 1
    No. The garbage collector works exactly the same way with a SqlConnection as it does with any other heap-allocated object. It will remove it from memory whether or not you call Dispose. Calling Dispose has absolutely zero effect on whether or not the garbage collector will remove your object from memory or when. The only difference is that if you call Dispose early, the other important non-memory resources will be cleaned up early.
    – mqp
    Commented Dec 13, 2011 at 19:57
2

using is only allowed by the language for situations where you should almost certainly be doing it - that is, classes which implement IDisposable. You're free to write code that implements IDisposable needlessly if you really want to, but almost all libraries will only do it for a reason - because the object needs to be cleaned up. You can write the dispose logic yourself if you want to, but there is really no benefit because using does exactly the same thing, it's just clearer.

Your definition of 'bloat' is an interesting one. While I understand that boring plumbing code is undesirable, that just makes it a candidate for walling up in a base class, not excluding it altogether. If boring code is proliferating, it can be an indication that you are violating DRY and should aim to refactor so that you specify the behaviour only once for everything that needs it.

1
  • I've not seen try/catch/finally and using statements in base classes very often. quite often im writing logic in an MVC controller action and I use db persistence objects, services, etc. and trying to find the cleanest way to handle exceptions and garbage without adding tons of catch blocks nested down the page... i'm not violating dry at all. Commented Dec 13, 2011 at 20:00
2

Every exception gets handled. You just get to choose if it kills your program or not, and if not, what you want to do after that. I regularly write code that throws exceptions all up and down the stack, catches the ones that it wants to recover from, and has a simple main method:

public static int main(string args[])
{
    try {
        DoSomethingUseful(args);
        return 0;
    } catch (Exception e) {
        Console.Write("Uncaught exception percolated to top level: " + e);
    }
    return 1;
}
2

Nested levels of try catch must be avoided if all they do is nothing but log the exceptions. If object supports IDisposable then it must be wrapped in using block.

Exceptions must be caught mostly on root entry points, instead catching and rethrowing at various levels. It just makes code little more complex to analyze stack traces.

Try-finally must be used only if resource object does not support IDisposable, or to turn some Boolean switches or to synchronize threads.

Don't ever use exceptions as part of logic, for example if I catch exception a then follow method b else follow method c, something like that. Other example include using exception in place of return type. Imagin you have ValidateUserPass method that returns void and throws exception instead of returning proper result. I know it is argueous but invalid logic is a user error and exception should mostly be used for system or network failure.

Do not throw generic exception Exception, but use proper encapsulated class for it.

At root level, catch all exceptions because it must be logged, and at intermediate level, do not catch exception unless you want to continue the logic in alternate way.

For example,

 try{
     connectToDevice()
     return;
 }catch(IOException ex){

 }
 connectToDatabase();

Here if I will catch all exception then I will eat up or catch wrong exception if I will put all in try block. Here in case my device connection fails I want to connect to database and work in offline mode. This is part of the logic only if device fails to connect.

But if user has entered a null as an address string, that should not be caught here because it is not an error to connecting device. My null pointer exception should be caught at root level, and not in this code.

For logging, catch all exceptions.

For alternative branch of execution, catch only one type of exception.

3
  • +1 Thank you. This is a little more what I was looking for. Some people have said to me 'catch everything an object might throw', others say 'catch e only at the top for clean code', etc... I'm trying to learn best practices... exception as logic is no concern here, but your first three comments are helpful. thank you. Commented Dec 13, 2011 at 19:57
  • I have updated my answer.
    – Akash Kava
    Commented Dec 14, 2011 at 4:35
  • "Don't ever use exceptions as part of logic,", cancellationTokens are designed to throw when cancelled. So I think it is exaggerated to never use exceptions for logic. I think avoid whenever possible would be a better claim.
    – JonasH
    Commented Jul 29, 2022 at 8:42
2

To address the part of your question about what exceptions you should catch, have a look at this article:

How to Design Exception Hierarchies

Despite the title, it's very useful in understanding the different types of exceptions. There are three types, and they should be handled in three different ways:

  • Usage error. This is due to an error in coding - fix your code.
  • Logical error. An unavoidable condition, such as file not found - handle the exception.
  • System failure. A system error such as out of memory - shut down the program.

(File not found is unavoidable because even if you call File.Exists(), it's possible someone could delete the file just before you access it. So to really be correct, you still need to handle it.)

The only case where you definitely need try/catch blocks is for logical errors. In those cases, you can't guarantee avoiding the exception, so you need to handle it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.