Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharpNotesForProfessionals.pdf
Скачиваний:
66
Добавлен:
20.05.2023
Размер:
6.12 Mб
Скачать

LogManager.Log(ex.ToString());

}

Do not catch exceptions that you cannot handle

Many resources, such as this one, strongly urge you to consider why you are catching an exception in the place that you are catching it. You should only catch an exception if you can handle it at that location. If you can do something there to help mitigate the problem, such as trying an alternative algorithm, connecting to a backup database, trying another filename, waiting 30 seconds and trying again, or notifying an administrator, you can catch the error and do that. If there is nothing that you can plausibly and reasonably do, just "let it go" and let the exception be handled at a higher level. If the exception is su ciently catastrophic and there is no reasonable option other than for the entire program to crash because of the severity of the problem, then let it crash.

try

{

//Try to save the data to the main database.

}

catch(SqlException ex)

{

//Try to save the data to the alternative database.

}

//If anything other than a SqlException is thrown, there is nothing we can do here. Let the exception bubble up to a level where it can be handled.

Section 76.4: Exception Anti-patterns

Swallowing Exceptions

One should always re-throw exception in the following way:

try

{

...

}

catch (Exception ex)

{

...

throw;

}

Re-throwing an exception like below will obfuscate the original exception and will lose the original stack trace. One should never do this! The stack trace prior to the catch and rethrow will be lost.

try

{

...

}

catch (Exception ex)

{

...

throw ex;

}

Baseball Exception Handling

One should not use exceptions as a substitute for normal flow control constructs like if-then statements and while loops. This anti-pattern is sometimes called Baseball Exception Handling.

GoalKicker.com – C# Notes for Professionals

453

Here is an example of the anti-pattern:

try

{

while (AccountManager.HasMoreAccounts())

{

account = AccountManager.GetNextAccount(); if (account.Name == userName)

{

//We found it

throw new AccountFoundException(account);

}

}

}

catch (AccountFoundException found)

{

Console.Write("Here are your account details: " + found.Account.Details.ToString());

}

Here is a better way to do it:

Account found = null;

while (AccountManager.HasMoreAccounts() && (found==null))

{

account = AccountManager.GetNextAccount(); if (account.Name == userName)

{

//We found it found = account;

}

}

Console.Write("Here are your account details: " + found.Details.ToString());

catch (Exception)

There are almost no (some say none!) reasons to catch the generic exception type in your code. You should catch only the exception types you expect to happen, because you hide bugs in your code otherwise.

try

{

var f = File.Open(myfile);

// do something

}

catch (Exception x)

{

// Assume file not found

Console.Write("Could not open file");

// but maybe the error was a NullReferenceException because of a bug in the file handling code?

}

Better do:

try

{

var f = File.Open(myfile);

// do something which should normally not throw exceptions

}

catch (IOException)

{

Console.Write("File not found");

GoalKicker.com – C# Notes for Professionals

454

}

// Unfortunatelly, this one does not derive from the above, so declare separatelly catch (UnauthorizedAccessException)

{

Console.Write("Insufficient rights");

}

If any other exception happens, we purposedly let the application crash, so it directly steps in the debugger and we can fix the problem. We mustn't ship a program where any other exceptions than these happen anyway, so it's not a problem to have a crash.

The following is a bad example, too, because it uses exceptions to work around a programming error. That's not what they're designed for.

public void DoSomething(String s)

{

if (s == null)

throw new ArgumentNullException(nameof(s));

// Implementation goes here

}

try

{

DoSomething(myString);

}

catch(ArgumentNullException x)

{

//if this happens, we have a programming error and we should check

//why myString was null in the first place.

}

Section 76.5: Basic Exception Handling

try

{

/* code that could throw an exception */

}

catch (Exception ex)

{

/* handle the exception */

}

Note that handling all exceptions with the same code is often not the best approach. This is commonly used when any inner exception handling routines fail, as a last resort.

Section 76.6: Handling specific exception types

try

{

/* code to open a file */

}

catch (System.IO.FileNotFoundException)

{

/* code to handle the file being not found */

}

catch (System.IO.UnauthorizedAccessException)

{

/* code to handle not being allowed access to the file */

GoalKicker.com – C# Notes for Professionals

455

}

catch (System.IO.IOException)

{

/* code to handle IOException or it's descendant other than the previous two */

}

catch (System.Exception)

{

/* code to handle other errors */

}

Be careful that exceptions are evaluated in order and inheritance is applied. So you need to start with the most specific ones and end with their ancestor. At any given point, only one catch block will get executed.

Section 76.7: Aggregate exceptions / multiple exceptions from one method

Who says you cannot throw multiple exceptions in one method. If you are not used to playing around with AggregateExceptions you may be tempted to create your own data-structure to represent many things going wrong. There are of course were another data-structure that is not an exception would be more ideal such as the results of a validation. Even if you do play with AggregateExceptions you may be on the receiving side and always handling them not realizing they can be of use to you.

It is quite plausible to have a method execute and even though it will be a failure as a whole you will want to highlight multiple things that went wrong in the exceptions that are thrown. As an example this behavior can be seen with how Parallel methods work were a task broken into multiple threads and any number of them could throw exceptions and this needs to be reported. Here is a silly example of how you could benefit from this:

public void Run()

{

try

{

this.SillyMethod(1, 2);

}

catch (AggregateException ex)

{

Console.WriteLine(ex.Message);

foreach (Exception innerException in ex.InnerExceptions)

{

Console.WriteLine(innerException.Message);

}

}

}

private void SillyMethod(int input1, int input2)

{

var exceptions = new List<Exception>();

if (input1 == 1)

{

exceptions.Add(new ArgumentException("I do not like ones"));

}

if (input2 == 2)

{

exceptions.Add(new ArgumentException("I do not like twos"));

}

if (exceptions.Any())

{

throw new AggregateException("Funny stuff happended during execution", exceptions);

}

GoalKicker.com – C# Notes for Professionals

456