Catching exceptions correctly .net - c#

I'm working in a new place and seeing a lot of things in the code I'm not comfortably with.
I've been seeing a lot of code like this:
try
{
dAmount = double.Parse(((TextBox)control.Items[someRow].FindControl("txtControl")).Text);
}
catch
{
dAmount = 0;
}
I read stackoverflow and the like frequently and I'm aware that is not right, but I'm debating myself about the best way to handle this.
The developer obviously thought setting dAmount=0; was a good way to handle the exception, but seeing how double.Parse can only throw 3 exceptions(ArgumentNullException, FormatException, OverflowException) and if we add the NullReferenceException in case the FindControl or other object comes back null, then it seems to me I get to cover all the cracks, however the code looks kind of ugly, and I'm looking for suggestions, a better approach maybe?
This is what I came out with
try
{
dAmount = double.Parse(((TextBox)control.Items[someRow].FindControl("txtControl")).Text);
}
catch ( NullReferenceException nullRefEx )
{
dAmount = 0;
nwd.LogError("***Message: " + nullRefEx.Message + " ***Source: " + nullRefEx.Source + " ***StackTrace: " + nullRefEx.StackTrace);
}
catch ( ArgumentNullException argNullEx )
{
dAmount = 0;
nwd.LogError("***Message: " + argNullEx.Message + " ***Source: " + argNullEx.Source + " ***StackTrace: " + argNullEx.StackTrace);
}
catch ( FormatException frmEx )
{
dAmount = 0;
nwd.LogError("***Message: " + frmEx.Message + " ***Source: " + frmEx.Source + " ***StackTrace: " + frmEx.StackTrace);
}
catch ( OverflowException ovrEx)
{
dAmount = 0;
nwd.LogError("***Message: " + ovrEx.Message + " ***Source: " + ovrEx.Source + " ***StackTrace: " + ovrEx.StackTrace);
}
BTW I don't control the logging function is from another team in here, I know is kind of ugly.
What about saving the exception in a general Exception and having only one nwd.LogError call at the end? any suggestions are welcome.

You're doing exactly the same thing with all of your exceptions, which rather defeats the purpose of catching them all separately. You should be catching different exceptions if you intend to actually do something different with each type.
Your finally block will run and zero out your value even if there are no exceptions.
You shouldn't be using exceptions for control flow in the first place; you should be writing the code so that no exceptions are thrown at all if the user inputs invalid data, rather than throwing and catching an exception for a non-exceptional use-case. In this context that's as simple as using double.TryParse.
Exceptions such as null reference/null argument exceptions, overflow exceptions, etc. should pretty much never be caught at all outside of a top level method that logs any fatal exceptions before failing gracefully. These are boneheaded exceptions; if you're getting them it's a sign that you have a bug in your code that you need to fix, not a problem that you should try to sweep under the rug and continue executing your program.

You should use Double.TryParse instead.
double number;
if (Double.TryParse(value, out number))
dAmount = number;
else
dAmount=0;
This is cleaner and avoids exceptions altogether.

Don't use finally because that will always set dAmount = 0, even if an exception isn't thrown. If you want to catch a general Exception, then a single catch (Exception ex) will suffice. If you want to catch those specifically, I would do the following:
try
{
// code here
...
}
catch (Exception ex)
{
if (ex is NullReferenceException || ex is ArgumentNullException
|| ex is FormatException || ex is OverflowException)
{
// handle ex
...
return;
}
throw;
}
In your case, your best option is to probably use Double.TryParse, like so:
double amount;
if(Double.TryParse(someValue, out amount)
{
// do something with amount...
}
else
{
// handle the error condition here
}

Catch a single exception that covers all those instances. The message in the log file should distinguish which type of exception was thrown.
string s = ....;
double d = 0;
try {
d = Double.Parse(s);
} catch (Exception ex) {
//Set default value
nwd.LogError("***Message: " + ex.Message + " ***Source: " + ex.Source + "***StackTrace: " + ex.StackTrace);
}
The key the other answers are missing is that he wants to log the exception... TryParse won't tell you why it didn't parse correctly, it just returns false and 0 in the out param.
The real question here is... is zero actually special? I.e. is 0 not a valid number for a person to put into the text box? How would the rest of the program tell whether the user entered a zero, or the zero was a result of the default value being returned on parse error?
The better plan, and one I use when I am getting a value object and might fail is to use a nullable...
string s = ....;
double? d = null;
try {
d = Double.Parse(s);
} catch (Exception e) {
nwd.LogError("***Message: " + ex.Message + " ***Source: " + ex.Source + "***StackTrace: " + ex.StackTrace);
}
return d;
Now the caller knows for a fact that an error occurred when you return null (versus some "magic value"). Using "magic values" unnecessarily complicates the semantics.
Even better is to simply let the error propagate (or re-throw it) to where it can actually be handled reasonably. What part of your code currently checks for that 0 "magic value" and does something different based on it? That's where your catch should be (or, catch and log and then rethrow).

Try/Catch isn't there to control flow of your methods. If you can't redesign whole thing then I'd do something like this.
public double Parse(...)
{
double value = 0;
string text = ((TextBox)control.Items[someRow].FindControl("txtControl")).Text;
double.TryParse(text, out value);
return value;
}

Related

C#, While Loop, Try Catch, Log, Continue

I'm writing a mobile application that will actually be running as a Task in ArcGIS, but my question is basically C# error handling. I'm using a while loop to loop through records and using StreamWriter to log a failed attempt to read the data. I've broken each field into its own seperate try catch so that I can write which record and which field caused the failure to the log file. Example below.
try
{
TrafficLanesCount = Convert.ToByte(ModifiedFDR.GetValue(ModifiedFDR.GetOrdinal("TRAFICLN")));
}
catch
{
sw.WriteLine(DOTNumber + " " + "TrafficLanesCount");
}
I'm saving at the end of the while loop, but if anything fails, I want to log it, skip that record, and continue on with the next record. Does anyone know a way to do this?
Instead of
sw.WriteLine(DOTNumber + " " + "TrafficLanesCount");
You could use
File.AppendAllText("path/to/log.txt", DOTNumber + " " + "TrafficLanesCount" + nvironment.NewLine);
To write the lines in realtime. This way, your logs would be persisted within every catch..
I also want to point out, that there are very mature Frameworks for Logging. For example NLog or Log4net to name two of the more popular ones.
Using the code provided, I would go with something like this:
var errorList = new List<Exception>();
foreach(var record in records)
{
try
{
try
{
TrafficLanesCount = Convert.ToByte(ModifiedFDR.GetValue(ModifiedFDR.GetOrdinal("TRAFICLN")));
}
catch
{
throw new Exception(new string(DOTNumber + " " + "TrafficLanesCount"));
}
}
catch (Exception exp)
{
errorList.Add(exp);
}
}
And afterwords, you can loop through the exceptions in the list and handle them at that point.

Trying to catch a FormatException error fails. Only Exception can catch it?

I have a method that accepts random data at runtime (it could be string, int, etc.) and then it tries adding this data to a list of a random type (e.g. List<byte>, List<int>, etc.).
Due to the unpredictable nature of this, I have added 2 error handling routines. One to catch a FormatException error and do some specific actions to fix the data. And secondly, I've also added a general catch-all for Exception so if another error I'm not expecting is thrown, I can display the details and exit the application gracefully.
The weird issue I'm having (or at least it seems weird to me), is that whenever a FormatException error is thrown, my Catch (FormatException ex) does nothing and instead the error is being caught by Catch (Exception ex). So rather than being able to handle the error properly, the application exits instead.
To help isolate this, I've created a small example C# WinForms program that replicates the issue.
Here's the main form code:
private void button1_Click(object sender, EventArgs e)
{
// This works normally
ErrorClass.TestCatchFormatException<string, string>("abc", "def");
// This also works normally
ErrorClass.TestCatchFormatException<int, int>("123", "456");
// This should raise a FormatException error but only Exception catches it???
ErrorClass.TestCatchFormatException<int, string>("abc", "456");
}
And here's the code for my 2 classes:
public class DataClass<T, U>
{
public T Data1 { get; set; }
public U Data2 { get; set; }
}
public static class ErrorClass
{
public static void TestCatchFormatException<T, U>(dynamic inputString, dynamic inputString2)
where T : IComparable<T>
where U : IComparable<U>
{
try
{
List<DataClass<T, U>> theList = new List<DataClass<T, U>>();
TypeConverter converter1 = TypeDescriptor.GetConverter(typeof(T));
TypeConverter converter2 = TypeDescriptor.GetConverter(typeof(U));
theList.Add(new DataClass<T, U>
{
Data1 = converter1.ConvertFrom(inputString.ToString()),
Data2 = converter2.ConvertFrom(inputString2.ToString())
});
MessageBox.Show(
"Data1 Value is: " + theList[0].Data1.ToString() + "\n"
+ "Data1 Type is: " + theList[0].Data1.GetType().ToString() + "\n"
+ "Data2 Value is: " + theList[0].Data2.ToString() + "\n"
+ "Data2 Type is: " + theList[0].Data2.GetType().ToString());
}
catch (FormatException ex)
{
// Catches nothing for some reason
MessageBox.Show("Caught FormatException\n\n" + ex.Message + "\n\n" + ex.InnerException);
}
catch (Exception ex)
{
// This catches the error but InnerException says the error is of type FormatException.
// Yet FormatException doesn't catch it???
MessageBox.Show("Caught Exception\n\n" + ex.Message + "\n\n" + ex.InnerException);
}
}
}
Even weirder is that when I look at the InnerException the catch-all Exception generates, it says this:
System.FormatException: Input string was not in a correct format.
So it is detecting it as a FormatException error. Yet for some reason, catching FormatException does nothing and only Exception can catch it.
Does anyone know what I'm doing wrong here?
Your FormatException is being wrapped in some other Exception type. That's why the .InnerException property has a FormatException. Try calling .GetType on the exception you are getting to see the wrapping type, and catch that instead.
BaseNumberConverter.ConvertFrom() internally calls a helper function to throw an exception:
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
...
catch (Exception e) {
throw FromStringError(text, e);
}
}
return base.ConvertFrom(context, culture, value);
}
The implementation of FromStringError is:
internal virtual Exception FromStringError(string failedText, Exception innerException) {
return new Exception(SR.GetString(SR.ConvertInvalidPrimitive, failedText, TargetType.Name), innerException);
}
So it throws an Exception that wraps the actual FormatException and adds some additional information (namely the value it's trying to convert and the type it's trying to convert to). Why it doesn't throw another FormatException I don't know.
Prior to C# 6 there was no way to create a catch that would catch based on the InnerException. You'd have to catch Exception and inspect the InnerException type to handle it differently.
With C# 6 you can use an exception filter:
catch (Exception ex) when (ex.InnerExcpetion is FormatException)
{
// Catches nothing for some reason
MessageBox.Show("Caught FormatException\n\n" + ex.Message + "\n\n" + ex.InnerException);
}
catch (Exception ex)
{
// This catches the error but InnerException says the error is of type FormatException.
// Yet FormatException doesn't catch it???
MessageBox.Show("Caught Exception\n\n" + ex.Message + "\n\n" + ex.InnerException);
}

Exception that is handled is still thrown?

My code:
public void CPUStats()
{
var cpuCounter = new PerformanceCounter("Processor", "% Processor Time",
"_Total");
var ramCounter = new PerformanceCounter("Memory", "Available MBytes");
ramCounter.NextValue();
cpuCounter.NextValue();
System.Threading.Thread.Sleep(1000);
string cpusage = cpuCounter.NextValue().ToString();
string ramusage = ramCounter.NextValue().ToString();
//Occurs here v
try
{
//exception thrown here v
cpuLabel.Invoke((MethodInvoker)(() => cpuLabel.Text = "CPU: " +
cpusage.Remove(cpusage.IndexOf('.')) + "%"));
}
catch(ArgumentOutOfRangeException)
{
cpuLabel.Invoke((MethodInvoker)(() => cpuLabel.Text = "CPU: " +
cpusage + "%"));
}
ramLabel.Invoke((MethodInvoker)(() => ramLabel.Text = "Free RAM: " +
ramusage + "mb"));
}
At times my application will stop at the cpuLabel invoke and throw an "ArgumentOutOfRangeException" when it was handled and fixed in my code. I tried increasing the timer that activates the thread that activates CPUStats(), but to no avail.
Why would this happen?
Update:
Sorry, I should read your code more carefully. Your try-catch wrapped the delegate invocation which may not in the current context of thread. I think what you want to do should be:
public void CPUStats() {
var cpuCounter= ..
...
MethodInvoker m=() => {
try {
cpuLabel.Text="CPU: "+cpusage.Remove(cpusage.IndexOf('.'))+"%";
}
catch(ArgumentOutOfRangeException) {
cpuLabel.Text="CPU: "+cpusage+"%";
}
};
cpuLabel.Invoke(m);
...
}
Have a look of SynchronizationContext and also Control.InvokeRequired.
Again, avoid try-catch if there's a better way to check for possible errors.
#competent_tech's answer is good, but I'd like to add a little bit.
There are some cases that a try-catch block would still throw:
You did not catch the exact exception it thrown
The handler rethrows the original exception
The handler causes another exception
In your case, it hits the catch block and did not rethrow the original one, that is, it met the third circumstance: the handler causes another exception.
If you can address the problem causes the exception and avoid it , then don't design it in this way.
Mr. Lippert wrote a good article about the exceptions, you might want to have a look at:
http://ericlippert.com/2008/09/10/vexing-exceptions/
The issue is being caused by this code:
cpusage.Remove(cpusage.IndexOf('.')
You need to ensure that cpusage has length and contains a '.' before attempting to call .Remove.
You can prove this by running a simple app test:
var cpuusage = "0";
System.Diagnostics.Debug.WriteLine(cpuusage.Remove(cpuusage.IndexOf('.')));
This will throw the ArgumentOutOfRangeException exception.
If you modify the code to be:
var cpuusageforlabel = "CPU: ";
if (!string.IsNullOrEmpty(cpuusage)) {
var index = cpuusage.IndexOf('.');
if (index != -1) {
cpuusageforlabel += cpuusage.Remove(index);
} else {
cpuusageforlabel += cpuusage;
}
} else {
cpuusageforlabel += "0";
}
cpuusageforlabel += "%";
cpuLabel.Invoke((MethodInvoker)(() => cpuLabel.Text = cpuusageforlabel));
You address the exception and won't have to catch the argument out of range exception.
Most probable reason is that cpusage is null or empty or without .. So when you get exception and try to use same variable again, you get the second exception.
Check cpusage before using it and don't use catch the way you are using.

C# if exception is caught will it reach my return statement?

In System.IO there is a function:
string File.ReadAllText( string path );
I am trying to write a function that would call File.ReadAllText, take care of all possible exceptions and return true/false and store error message.
What I have is this:
public static class FileNoBS
{
public static bool ReadAllText( string path, out string text, out string errorMessage )
{
errorMessage = null;
text = null;
bool operationSuccessful = false;
try
{
text = System.IO.File.ReadAllText( path );
operationSuccessful = true;
}
catch ( ArgumentNullException e )
{
errorMessage = "Internal software error - argument null exception in FileNoBs.ReadAllText\nMessage: " + e.Message;
}
catch ( ArgumentException e )
{
errorMessage = "Internal software error - path is a zero-length string, contains only white space, or contains one or more invalid characters as defined by InvalidPathChars in FileNoBs.ReadAllText.\nMessage: " + e.Message;
}
catch ( PathTooLongException e )
{
errorMessage = "The specified path was too long.\nMessage: " + e.Message;
}
catch ( DirectoryNotFoundException e )
{
errorMessage = "The specified directory was not found.\nMessage: " + e.Message;
}
catch ( FileNotFoundException e )
{
errorMessage = "The file specified in path was not found.\nMessage: " + e.Message;
}
catch ( IOException e )
{
errorMessage = "An I/O error occurred while opening the file.\nMessage: " + e.Message;
}
catch ( UnauthorizedAccessException e )
{
errorMessage = #"UnauthorizedAccessException
path specified a file that is read-only.
-or-
This operation is not supported on the current platform.
-or-
path specified a directory.
-or-
The caller does not have the required permission.\nMessage: " + e.Message;
}
catch ( NotSupportedException e )
{
errorMessage = "path is in an invalid format.\nMessage: " + e.Message;
}
catch ( SecurityException e )
{
errorMessage = "You do not have the required permission.\nMessage: " + e.Message;
}
return operationSuccessful;
}
}
I don't understand how how control flow goes with functions that return value.
Let's say UnauthorizedAccessException gets caught, errorMessage is set to
errorMessage = "You do not have the required permission..."
I know that finally gets executed every time, but compiler won't let me do return inside finally block. So will my return get reached or not?
Another question is how to simplify this while still following official guidelines:
"In general, you should only catch those exceptions that you know how to recover from. "
I dread going through all functions that I will need from File class (Move, Copy, Delete, ReadAllText, WriteAllText) and then Directory class and doing all these long blocks of code just to catch all exceptions I don't care about and not catch too many of them cause Microsoft says it's bad.
Thank you.
EDIT: I got comments like this is not handling exceptions this is "something else".
I am client for my code and I want to do something like this:
if ( !FileNoBS.ReadAllText( path, text, errorMessage ) ) {
MessageBox.Show( errorMessage );
return;
}
// continue working with all errors taken care of - don't care for whatever reason file wasn't opened and read, user is notified and I am moving on with my life
Your return will be reached as there isn't a return in the try block or the catch block.
Generally, you only want to catch exceptions that you expect may occur and have a way of handling them. For example, you may want to handle the file not being found from the given path and return a default file instead. You should allow other exceptions not to be caught so you know that something unexpected has happened and not hide it by catching all exceptions.
As I said in my comment, you are better off handling the exceptions at a higher level and simply displaying the exception message rather than manually setting each message. I think in this case the message from the exception will be descriptive enough.
public static class FileNoBS
{
public static string ReadAllText(string path)
{
return System.IO.File.ReadAllText( path );
}
}
then use it like this at some higher level in your application. I typically have a general handler to handle all application exceptions and log them and display a message box if necessary.
try
{
var text = FileNoBS.ReadAllText("file.ext");
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
Instead of catching the exceptions you should try to avoid the situation that will lead to those exceptions being thrown in the first place. In your case you should have some input validation before calling ReadAllText
never accept a path that is null - you know this will lead to an exception so handle it before it does
never accept a path that leads to a file that does not exist - use File.Exists(path) prior to the call
never accept a malformed path E.g. the empty string or one with invalid characters - this will lead to an exception
These tests should be performed where the input originates. That is if the user types them in, validate them before using them. If they come from a DB or somewhere else validate there before use. If it's not user input they are all indications of a system error and should be treated as such, not as something the user should worry about.
Security exceptions can be somewhat harder to test up front and in many cases it is exceptional to get a violation and therefor perfectly ok to get an exception. It shouldn't crash the program of course but be handled with an errormessage to the user (if it's based on user input, if it's system generated data that leads to this, it's an idication of a system error that should be fixed at code level). It's often more appropriate to do this where the call happens than in some library method.
for IOExceptions they can be put into two buckets. Recoverable once (usually a retry) and unrecoverable once. As a minimum give the user feedback on the exception, so the user might have the option of retrying.
A very general rule that should be part of the error correction logic is to never have invalid data floating around the system. Make sure that all objects manage the invariants (Tools are available for this such as code contracts). Reject invalid input from the user (or other systems) when they are received instead of when they result in an exception.
If you do all the input validation and still have E.g. ArgumentNullException then that points to an error in the logic of the program, something that you want to be able to easily find in a test and correct before you release the bug. You shouldn't try and mask this error.
Provided no other error occurs, yes.
I'd add at the end:
catch (Exception e)
{
errormessage = "An unexpected error has occured.";
}
return operationSuccessful;
Though, this will always return the successful even if you got an error. I'm not sure if that's what you want, or if your variables are badly named.
The return statement is going to be called in case of any exception in your code, before it is placed at the end of the program before it exits.
I will suggest placing a single exception handler with a high level Exception type, like the 'Exception' type itself, and print or log the exception message. Specifying so many exception handlers in each method is going to take a lot of energy which your should actually put in the method itself.
try
{
return ReadAllText("path", "text", "error");
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
return false;
So if the method gets called, it will return immediately, otherwise the exception gets printed/logged and the method will return false.
You can however, mention a couple or few explicit exception handlers in some cases, where you think it will be beneficial.
Yes It will return the value.
But, better you handle return value in finally statement.
If in any case you want to return operationSuccessful value, then write finally block after catch blocks as follows,
finally
{
return operationSuccessful;
}

Catching exception types given as type parameters in C# 2.0

In the following method, the first catch block is never run, even when an exception of type ExceptionType is thrown:
/// <summary>
/// asserts that running the command given throws an exception.
/// </summary>
public static void Throws<ExceptionType>(ICommand cmd)
where ExceptionType : Exception
{
// Strangely, using 2 catch blocks on the code below makes the first catch block do nothing.
try
{
try
{
cmd.Execute();
}
catch (ExceptionType)
{
return;
}
}
catch (Exception f)
{
throw new AssertionException(cmd.ToString() + " threw an exception of type " + f.GetType() + ". Expected type was " + typeof(ExceptionType).Name + ".");
}
throw new AssertionException(cmd.ToString() + " failed to throw a " + typeof(ExceptionType).Name + ".");
}
as indicated by the following test:
[Test]
public void Test_Throws_CatchesSpecifiedException()
{
AssertThat.Throws<AssertionException>(
new FailureCommand()
);
}
using the following class:
class FailureCommand : ICommand
{
public object Execute()
{
Assert.Fail();
return null; // never reached.
}
public override string ToString()
{
return "FailureCommand";
}
}
giving the following output in NUnit:
TestUtil.Tests.AssertThatTests.Test_Throws_CatchesSpecifiedException:
FailureCommand threw an exception of type NUnit.Framework.AssertionException. Expected type was AssertionException.
I also tried using 2 catch blocks for 1 try block (instead of nesting a try/catch within an outer try), but got the same results.
Any ideas as to how to catch the exception specified as a type parameter in one catch block, but catch all other exceptions in the other?
Works fine for me in this test:
using System;
using System.IO;
class Test
{
static void Main()
{
Throws<ArgumentNullException>(() => File.OpenText(null));
}
public static void Throws<ExceptionType>(Action cmd)
where ExceptionType : Exception
{
try
{
try
{
cmd();
}
catch (ExceptionType)
{
Console.WriteLine("Caught!");
return;
}
}
catch (Exception f)
{
Console.WriteLine("Threw an exception of type " + f.GetType()
+ ". Expected type was " + typeof(ExceptionType) + ".");
}
Console.WriteLine("No exception thrown");
}
}
How certain are you that the two AssertionException exceptions are identical? Are they definitely in the same namespace (print typeof(ExceptionType) rather than just the Name property)? Are they from the same assembly? I wouldn't be surprised to find some oddities due to multiple test framework versions coexisting...
Try it with an exception other than AssertionException to make life simpler.
See this thread for more info:
Why can't I catch a generic exception in C#?
Seems to be a bug:
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=362422&wa=wsignin1.0
I haven't debugged this code or looked at it too long but could it be that you first "return" statement is causing the code to HALT and leave the method. Therefore, the exception is "caught" or "handled" (however you look at it) and the outer try/catch block is never executed.
Are you trying to do this?
try
{
try
{
cmd.Execute();
}
catch (ExceptionType)
{
throw;
}
}
catch (Exception f)
{
throw new AssertionException(cmd.ToString() + " threw an exception of type " + f.GetType() + ". Expected type was " + typeof(ExceptionType).Name + ".");
}

Categories