49

One thing that has bugged me with exception handling coming from Python to C# is that in C# there doesn't appear to be any way of specifying an else clause. For example, in Python I could write something like this (Note, this is just an example. I'm not asking what is the best way to read a file):

try { reader = new StreamReader(path); } catch (Exception) { // Uh oh something went wrong with opening the file for reading } else { string line = reader.ReadLine(); char character = line[30]; } 

From what I have seen in most C# code people would just write the following:

try { reader = new StreamReader(path); string line = reader.ReadLine(); char character = line[30]; } catch (Exception) { // Uh oh something went wrong, but where? } 

The trouble with this is that I don't want to catch out of range exception coming from the fact that the first line in the file may not contain more than 30 characters. I only want to catch exceptions relating to the reading of the file stream. Is there any similar construct I can use in C# to achieve the same thing?

7
  • 3
    I'm surprised no-one came up with an answer featuring goto! Commented Jul 24, 2009 at 12:53
  • 13
    catching general Exceptions is never a good idea, but you can specifiy what types of exceptions are caught. Commented Jul 24, 2009 at 12:54
  • 4
    I guess I didn't make it clear in the original question but what I was getting at is I don't want to mask bugs in the try {} section by catching them and letting the program continue on it's way. I'd prefer it to crash so that I can fix the bug straight away. While also being able to handle cases that are out of my control (i.e. a file not present or not having access to it because it is on a network drive, etc...). Most solutions suggest catching IOException, but what if StreamReader throws some other type of exception that I may not have anticipated? Commented Jul 24, 2009 at 15:52
  • 1
    Martin, if you didn't anticpate it you should, in general, let it pass. Only handle what you understand and what you can deal with. C# gives you all the tools to handle what you want where you want, you have to bring the strategy. Commented Jul 24, 2009 at 16:04
  • 14
    This thread is rather disappointing. None of the answers propose a proper way of writing try/except/else from Python as try/catch/else in C#. Commented Aug 17, 2012 at 20:32

15 Answers 15

47

Catch a specific class of exceptions

try { reader = new StreamReader(path); string line = reader.ReadLine(); char character = line[30]; } catch (IOException ex) { // Uh oh something went wrong with I/O } catch (Exception ex) { // Uh oh something else went wrong throw; // unless you're very sure what you're doing here. } 

The second catch is optional, of course. And since you don't know what happened, swallowing this most general exception is very dangerous.

Sign up to request clarification or add additional context in comments.

8 Comments

I'd rather go for a separate try{...}catch(Exception){...} around the read. With unchecked exceptions, you never know what else the Reader might throw besides IOException -- now or in the future.
In that case, the Exception will bubble up through the caller and get caught somewhere else. Always handle an Exception when you're ready to handle it.
Tiberiu, the "you never know what else" goes with the IndexOutOfRange from char[30] etc. No need to treat the ReadLine different unless there is a good reason. But catching the 'remainder' could happen higher up.
The question was specifically how to catch (all) exceptions related to reading the file stream (not necessarily limited to IOException). In this case, you can add a try{} around the read, catch all exceptions, then package them up in a ReadingStreamException and throw them again.
You really, really want to do yourself a favor and never catch+swallow general exceptions outside of general error-logging code. It's bound to lead to difficult to debug bugs once something new gets wrong that you hadn't considered that your existing code deals with incorrectly.
|
15

You could write it like:

bool success = false; try { reader = new StreamReader(path); success = true; } catch(Exception) { // Uh oh something went wrong with opening the file for reading } finally { if(success) { string line = reader.ReadLine(); char character = line[30]; } } 

5 Comments

Why put it in a finally block?
@ChieltenBrinke, I think Johan Kullbom wanted the code easy to understand. By putting lines in the finally, I could see that the code under if clause belongs to try and catch.
Finally is designed for cleanup of resources. It ought not be used off-label like that.
I think best answer for asked question is this one.
finally provides more grouping than not using it, but also restricts what you can do. You can for example not return from a finally-block
6

You can do this:

try { reader = new StreamReader(path); } catch (Exception) { // Uh oh something went wrong with opening the file for reading } string line = reader.ReadLine(); char character = line[30]; 

But of course, you will have to set reader into a correct state or return out of the method.

4 Comments

This only catches errors in Opening the reader, not in the actual reading .
Adrian, you are right, but the text says something different.
Comment says "with opening the file for reading", not with actual reading.
This is probably the closest answer to what was desired.
6

Catch more specific exceptions.

try { reader = new StreamReader(path); string line = reader.ReadLine(); char character = line[30]; } catch(FileNotFoundException e) { // thrown by StreamReader constructor } catch(DirectoryNotFoundException e) { // thrown by StreamReader constructor } catch(IOException e) { // some other fatal IO error occured } 

Further, in general, handle the most specific exception possible and avoid handling the base System.Exception.

Comments

5

You can do something similar like this:

bool passed = true; try { reader = new StreamReader(path); } catch (Exception) { passed = false; } if (passed) { // code that executes if the try catch block didnt catch any exception } 

Comments

3

You can nest your try statements, too

Comments

3

Exceptions are used differently in .NET; they are for exceptional conditions only.

In fact, you should not catch an exception unless you know what it means, and can actually do something about it.

5 Comments

That advice applies just as strongly to Python as .NET.
This doesn't answer the question.
@CraigMcQueen: is that the reason for your downvote?
Yes, that's the reason.
@CraigMcQueen: thanks - it's good to know why these things happen.
2

You can have multiple catch clauses, each specific to the type of exception you wish to catch. So, if you only want to catch IOExceptions, then you could change your catch clause to this:

try { reader = new StreamReader(path); string line = reader.ReadLine(); char character = line[30]; } catch (IOException) { } 

Anything other than an IOException would then propagate up the call stack. If you want to also handle other exceptions, then you can add multiple exception clauses, but you must ensure they are added in most specific to most generic order. For example:

try { reader = new StreamReader(path); string line = reader.ReadLine(); char character = line[30]; } catch (IOException) { } catch (Exception) { } 

Comments

1

More idiomatically, you would employ the using statement to separate the file-open operation from the work done on the data it contains (and include automatic clean-up on exit)

try { using (reader = new StreamReader(path)) { DoSomethingWith(reader); } } catch(IOException ex) { // Log ex here } 

It is also best to avoid catching every possible exception -- like the ones telling you that the runtime is about to expire.

1 Comment

Martin Sherburn was asking how to write try / catch / else in C#, not how to write file-openers properly.
1

Is there any similar construct I can use in C# to acheive the same thing?

No.

Wrap your index accessor with an "if" statement which is the best solution in your case in case of performance and readability.

if (line.length > 30) { char character = line [30]; } 

Comments

1

After seeing the other suggested solutions, here is my approach:

try { reader = new StreamReader(path); } catch(Exception ex) { // Uh oh something went wrong with opening the file stream MyOpeningFileStreamException newEx = new MyOpeningFileStreamException(); newEx.InnerException = ex; throw(newEx); } string line = reader.ReadLine(); char character = line[30]; 

Of course, doing this makes sense only if you are interested in any exceptions thrown by opening the file stream (as an example here) apart from all other exceptions in the application. At some higher level of the application, you then get to handle your MyOpeningFileStreamException as you see fit.

Because of unchecked exceptions, you can never be 100% certain that catching only IOException out of the entire code block will be enough -- the StreamReader can decide to throw some other type of exception too, now or in the future.

Comments

1

There might not be any native support for try { ... } catch { ... } else { ... } in C#, but if you are willing to shoulder the overhead of using a workaround, then the example shown below might be appealing:

using System; public class Test { public static void Main() { Example("ksEE5A.exe"); } public static char Example(string path) { var reader = default(System.IO.StreamReader); var line = default(string); var character = default(char); TryElse( delegate { Console.WriteLine("Trying to open StreamReader ..."); reader = new System.IO.StreamReader(path); }, delegate { Console.WriteLine("Success!"); line = reader.ReadLine(); character = line[30]; }, null, new Case(typeof(NullReferenceException), error => { Console.WriteLine("Something was null and should not have been."); Console.WriteLine("The line variable could not cause this error."); }), new Case(typeof(System.IO.FileNotFoundException), error => { Console.WriteLine("File could not be found:"); Console.WriteLine(path); }), new Case(typeof(Exception), error => { Console.WriteLine("There was an error:"); Console.WriteLine(error); })); return character; } public static void TryElse(Action pyTry, Action pyElse, Action pyFinally, params Case[] pyExcept) { if (pyElse != null && pyExcept.Length < 1) { throw new ArgumentException(@"there must be exception handlers if else is specified", nameof(pyExcept)); } var doElse = false; var savedError = default(Exception); try { try { pyTry(); doElse = true; } catch (Exception error) { savedError = error; foreach (var handler in pyExcept) { if (handler.IsMatch(error)) { handler.Process(error); savedError = null; break; } } } if (doElse) { pyElse(); } } catch (Exception error) { savedError = error; } pyFinally?.Invoke(); if (savedError != null) { throw savedError; } } } public class Case { private Type ExceptionType { get; } public Action<Exception> Process { get; } private Func<Exception, bool> When { get; } public Case(Type exceptionType, Action<Exception> handler, Func<Exception, bool> when = null) { if (!typeof(Exception).IsAssignableFrom(exceptionType)) { throw new ArgumentException(@"exceptionType must be a type of exception", nameof(exceptionType)); } this.ExceptionType = exceptionType; this.Process = handler; this.When = when; } public bool IsMatch(Exception error) { return this.ExceptionType.IsInstanceOfType(error) && (this.When?.Invoke(error) ?? true); } } 

Comments

0

I have taken the liberty to transform your code a bit to demonstrate a few important points.

The using construct is used to open the file. If an exception is thrown you will have to remember to close the file even if you don't catch the exception. This can be done using a try { } catch () { } finally { } construct, but the using directive is much better for this. It guarantees that when the scope of the using block ends the variable created inside will be disposed. For a file it means it will be closed.

By studying the documentation for the StreamReader constructor and ReadLine method you can see which exceptions you may expect to be thrown. You can then catch those you finde appropriate. Note that the documented list of exceptions not always is complete.

// May throw FileNotFoundException, DirectoryNotFoundException, // IOException and more. try { using (StreamReader streamReader = new StreamReader(path)) { try { String line; // May throw IOException. while ((line = streamReader.ReadLine()) != null) { // May throw IndexOutOfRangeException. Char c = line[30]; Console.WriteLine(c); } } catch (IOException ex) { Console.WriteLine("Error reading file: " + ex.Message); } } } catch (FileNotFoundException ex) { Console.WriteLine("File does not exists: " + ex.Message); } catch (DirectoryNotFoundException ex) { Console.WriteLine("Invalid path: " + ex.Message); } catch (IOException ex) { Console.WriteLine("Error reading file: " + ex.Message); } 

2 Comments

"(Note, this is just an example. I'm not asking what is the best way to read a file)"
@NoctisSkytower: I'm not sure how to interpret your comment. I guess it can be read as a criticism of my answer but it took me a while to connect your quoted text with the text of the question. Interestingly, the text you have quoted was added to the question after I wrote my answer nine years ago.
0

Sounds like you want to do the second thing only if the first thing succeeded. And maybe catching different classes of exception is not appropriate, for example if both statements could throw the same class of exception.

try { reader1 = new StreamReader(path1); // if we got this far, path 1 succeded, so try path2 try { reader2 = new StreamReader(path2); } catch (OIException ex) { // Uh oh something went wrong with opening the file2 for reading // Nevertheless, have a look at file1. Its fine! } } catch (OIException ex) { // Uh oh something went wrong with opening the file1 for reading. // So I didn't even try to open file2 } 

Comments

-1

If you happen to be in a loop, then you can put a continue statement in the catch blocks. This will cause the remaining code of that block to be skipped.

If you are not in a loop, then there is no need to catch the exception at this level. Let it propagate up the call stack to a catch block that knows what to do with it. You do this by eliminating the entire try/catch framework at the current level.

I like try/except/else in Python too, and maybe they will get added to C# some day (just like multiple return values were). But if you think about exceptions a little differently, else blocks are not strictly necessary.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.