Throwing exceptions at multiple points (refactoring) - c#

I'm writing a function that takes user input, runs a procedure in our database, and compares the values. Along the way, I need to check that we've received proper input and then that the query has returned an acceptable value.
private void DoTheThing(int? userInput1, int? userInput2, int valuePassedIn)
{
if (userInput1 == null || userInput2 == null)
{
Exception ex = new Exception();
ex.Data.Add("Message", "You screwed up.");
throw ex;
}
var queryResult = 0; //execute a query using the non-null inputs
if (queryResult == null) //or otherwise doesn't return an acceptable value
{
Exception ex = new Exception();
ex.Data.Add("Message", "some other thing happened");
throw ex;
}
else
{
//We're good, so do the thing
}
}
A quick note about this: I'm aware of the argument against exceptions as flow control and that I'd be better off checking the user's input before I even get this far. I won't get into all the details, but please accept that I'm kind of stuck writing the function this way.
That having been said, here's my question:
Given that the only differences between the 2 exceptions here is the message and the time at which they are thrown, how can I clean this code up to be both DRY and avoid running unnecessary code after determining that there will be a problem?
I thought about using a goto and placing the error code there, but that really only moves the problem around. If I move the exception code to the bottom and check for a message variable (or something similar), then I'm just running code that doesn't need to be run in the first place.

I suggest not throwing Exception (which means something went wrong, no comments are available), but ArgumentNullException and InvalidOperationException classes. Another amendment is avoding arrow-head
antipattern:
private void DoTheThing(int? userInput1, int? userInput2, int valuePassedIn)
{
// What actually went wrong? An argument "userInput1" is null
if (null == userInput1)
throw new ArgumentNullException("userInput1");
else if (null == userInput2)
throw new ArgumentNullException("userInput2"); // ...or userInput2 is null
var queryResult = executeSomeQuery(userInput1, userInput2, valuePassedIn);
// What went wrong? We don't expect that null can be returned;
// so the operation "executeSomeQuery" failed:
// we've provided validated (not null) values and got unexpected return.
// Let it have been known.
if (null == queryResult)
throw new InvalidOperationException(
String.Format("Query ({0}, {1}, {2}) returned null when bla-bla-bla expected",
userInput1, userInput2, valuePassedIn));
// We're good, so do the thing
// Note that's there's no "arrow-head antipattern":
// we're not within any "if" or "else" scope
}
Edit: Since every *Exception is inherited from Exception you can put some info into Data:
Exception ex = new ArgumentNullException("userInput1");
ex.Data.Add("Some key", "Some value");
throw ex;
but often Message is a far better place to explain what had heppened.

You might be better off creating a BadInputException class and a NullQueryResultException class. These do two different things and throwing a specific exception is better than throwing a generic Exception(...). In fact I think FXCop or Visual Studio's Code Analysis will give you a warning about throwing generic Exceptions.
It's not really all that much new code to write.
public class BadInputException : Exception
{
public BadInputException()
{
this.Data.Add("Message", "You screwed up.")
}
}
Then instead of this:
Exception ex = new Exception();
ex.Data.Add("Message", "You screwed up.");
throw ex;
Do this:
throw new BadInputException();
Edit: moved the "You screwed up" message from the Message property to the Data collection to match what the OP wants.

I would create a method:
private void CheckResult(bool cond, string msg, string info) {
if (!cond)
return;
Exception ex = new Exception();
ex.Data.Add(msg, info);
throw ex;
}
and call
CheckResult(userInput1 == null || userInput2 == null, "Message", "You screwed up.");
and
CheckResult(queryResult == null, "Message", "You screwed up.");

I think the QuestionRefactoring Guard Clauses is helpful for you .
There are something about this in Replace Nested Conditional with Guard Clauses.
Hope it's useful.

Related

How to fix "cannot convert from 'bool' to 'string'" error?

i'm trying to mod an existing discord bot, and i'm getting this error when i try to run it. I'm trying to make one exception when it verifies if the message was sent from a bot or not.
private async Task MessageReceivedHandler(SocketMessage msg)
{
try
{
if (msg.Author.IsBot || !_bot.Ready.Task.IsCompleted) //no bots, wait until bot connected and initialized
throw new ArgumentNullException(msg.Author.Id == 1234)
return;
if (!(msg is SocketUserMessage usrMsg))
return;
#if !GLOBAL_NADEKO
// track how many messagges each user is sending
UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old);
#endif
var channel = msg.Channel as ISocketMessageChannel;
var guild = (msg.Channel as SocketTextChannel)?.Guild;
await TryRunCommand(guild, channel, usrMsg).ConfigureAwait(false);
}
catch (Exception ex)
{
_log.Warn("Error in CommandHandler");
_log.Warn(ex);
if (ex.InnerException != null)
{
_log.Warn("Inner Exception of the error in CommandHandler");
_log.Warn(ex.InnerException);
}
}
}```
You have a couple issues.
First:
throw new ArgumentNullException(msg.Author.Id == 1234)
The constructor for ArgumentNullException takes a string that is supposed to be "The name of the parameter that caused the exception". You are giving it a bool.
But you only have one argument, and by this line you already know it's not null. So maybe just ArgumentException is more appropriate, and write a sentence that describes why the argument is not acceptable.
Second, I don't think this does what you think it does:
if (msg.Author.IsBot || !_bot.Ready.Task.IsCompleted) //no bots, wait until bot connected and initialized
throw new ArgumentNullException(msg.Author.Id == 1234)
return;
If an if statement is not followed by { }, then only the one next line is considered part of the conditional code. (this is true of for, foreach, etc. - read more about "embedded statements" here)
In other words, this is the equivalent to what you have:
if (msg.Author.IsBot || !_bot.Ready.Task.IsCompleted) {
throw new ArgumentNullException(msg.Author.Id == 1234)
}
return;
It will either throw an exception, or return. All the code after the return will never be reached. I don't think that's what you want.
This is one reason I always use braces around single-line code blocks.

C# lambda null runtime binding

I'm running into an odd scenario that doesn't happen on my PC, but does for a coworkers.
I have this piece of code:
LoaderHelpers.SetStringValue<blah>(this, "x", $"x response in Header",
() => jsonData.x.response[0].value, false);
The problem is that sometimes, "jsonData.x" is null and, for my coworker a 'cannot bind to null at runtime exception' is thrown, but not for me there isn't. I have code to handle the null scenario, but it's like his code never gets to that point and fails at the call level.
jsonData is of type dynamic.
The method code that handles the null scenario:
public static void SetStringValue<T>(IValidate data, string propertyName,
string valuePath, Func<string> value, bool required)
{
if (data.GetType().GetProperty(propertyName) != null)
{
try
{
if (string.IsNullOrEmpty(value()))
{
if (required)
data.DataValidationErrors.Add($"{valuePath} can't be empty");
data.GetType().GetProperty(propertyName).SetValue(data, null);
}
else
{
data.GetType().GetProperty(propertyName).SetValue(data, value());
}
}
catch
{
//property doesn't exist
if (required)
data.DataValidationErrors.Add($"{valuePath} doesn't exist");
data.GetType().GetProperty(propertyName).SetValue(data, null);
}
}
else
{
throw new NullReferenceException($"In {data.GetType()} => SetStringValue. " +
$"Passed property {propertyName}, but property doesn't exist.");
}
}
Again. Works perfect for me, but not for him. I'm completely lost. Maybe I don't understand how the lamba/function parameters work 100%, but I thought it only got evaluated when value() is invoked.
I should also mention that when I debug this code, I can step into the Nuget package and when he hits the same line, he can't. This maybe a useful hint.
If jsonData (or jsonData.x) is null (as it seems to be at this point) it will crash and give you that error every time you call the method value().
You need to check why jsonData.x is null. Maybe it´s a race condition caused by another thread setting this value to null, maybe it´s because a bad jsonData initialization... Can´t say since that code is not here.
There are so many things wrong with your code, i can't resist.
First of all, instead of copy/pasting the same stuff over and over, you might want to use a variable:
var property = data.GetType().GetProperty(propertyName);
Second, you pass a Func<string> and execute it multiple times, why is it even a function then? Yet again, better only evaluate it once and use a variable...
var unwrapped = value();
That would solve the issue, that Roberto Vázquez' answer adresses.
Then you are misusing NullReferenceException, instead rather use a ArgumentException
Next issue, that valuePath is only used in the exception message, that is a poor design to my beliefs.
The generic T parameter isnt even used, so get rid of it.
Last but not least, that catch-block doing the exact thing that could possibily throw the exception again, i cant see any reason why you would do this.
Finnaly this whole thing becomes a little more clear but its still a mess.
public static void SetStringValue(IValidate data, string propertyName,
string valuePath, Func<string> value, bool required)
{
if(data == null)
throw new ArgumentNullException(nameof(data));
var property = data.GetType().GetProperty(propertyName);
if(property == null)
throw new ArgumentException($"In {data.GetType()} => SetStringValue. " +
$"Passed property {propertyName}, but property doesn't exist.");
var unwrapped = value();
try
{
if (string.IsNullOrEmpty(unwrapped))
{
if (required)
data.DataValidationErrors.Add($"{valuePath} can't be empty");
unwrapped = null; // this might be unecessary.
}
property.SetValue(data, unwrapped);
}
catch(Exception e)
{
// This is probably a bad idea.
property.SetValue(data, null);
if (required)
data.DataValidationErrors.Add(Atleast put a better message here. e.Message ...);
}
}

Ignoring exceptions vs throwing it explicitly

Is it alright to return an empty object in case of an exception or should we throw the exception so that caller may know what has gone wrong?
public async Task<UserInfoModel> GetUserInfoByRole(Role role)
{
UserModel userInfo = new UserModel();
try
{
// do something
}
catch (Exception ex)
{
// do logging
// throw;
}
return userInfo;
}
It depends if you are creating a class, component, ... for others to use, you obviously should throw an exception. because they need to know about it and handle the exception the way that suits them.
If it is a method in your own code, may be returning a null value would be sufficient, because you might just check the return value and if it is null you know that there was an error and you don't want to program break because of the exception, otherwise you will need another exception handling again.

How to treat and test flow control if not with exceptions with c#?

What's the right way to treat and test flow control on methods that are void if not with exceptions? I've seen that Microsoft do not recomend such practice so what's the right way?
This is how how I'm treating parameters that shouldn't be accepted in my method:
public void RentOutCar(ReservationInfo reservationInfo)
{
try
{
if (string.IsNullOrEmpty(reservationInfo.ReservationNumber) || string.IsNullOrWhiteSpace(reservationInfo.ReservationNumber))
{
throw new ArgumentException("Reservation Number is null or empty.");
}
if (reservationInfo == null)
{
throw new ArgumentNullException("Null Reservation info.");
}
if (reservationInfo.Car == null)
{
throw new ArgumentNullException("No car registered to rent.");
}
if (reservationInfo.RentalDatetime == DateTime.MinValue || reservationInfo.RentalDatetime == DateTime.MaxValue)
{
throw new ArgumentException("Rental Date has an unreal value.");
}
if (reservationInfo.Car.Mileage <0)
{
throw new ArgumentOutOfRangeException("Mileage can't be less than 0.");
}
reserverationsRegister.ReservationsDone.Add(reservationInfo);
}
catch (Exception)
{
throw;
}
}
This is not what Microsoft mean when they say you should not control flow with exceptions.
While the use of exception handlers to catch errors and other events
that disrupt program execution is a good practice, the use of
exception handler as part of the regular program execution logic can
be expensive and should be avoided.
In other words, you should not throw (and subsequently catch) exceptions in situations where the code in the try block is likely to throw and represents legitimate program logic.
A contrived example of controlling flow with exceptions may look like:
int x = GetUserInput();
try
{
MustAcceptPositiveInput(x);
}
catch (InputIsNonPositiveException)
{
MustAcceptNonPositiveInput(x);
}
The equivalent 'correct' code may look like:
int x = GetUserInput();
if (x > 0)
{
MustAcceptPositiveInput(x);
}
else
{
MustAcceptNonPositiveInput(x);
}
Exceptions should be reserved for exceptional situations, those which are not part of expected program execution. It results in more readable, less surprising and more performant code.
What you are doing in your code is fine (except for the redundant try-catch and faulty order of tests as #Clay mentions), you are validating inputs for exceptional values, those which your code was not meant to handle.
Throwing an exception if the inputs are not valid is fine. Test reservationInfo for null first - or your other tests will break in unexpected ways. Also - no point in wrapping your tests in a try/catch if all you're going to do is rethrow it.
This is not a "control flow" issue as described in the article you put in the comments - and throwing exceptions is appropriate here.
You might consider wrapping just the "working code" in a try/catch, but only if you can recover from (or maybe log) any exceptions:
try
{
reserverationsRegister.ReservationsDone.Add(reservationInfo);
}
catch( Exception ex )
{
LogError( ex );
throw;
}

Best practices for exception handling and safe coding

Say you were calling a method similar to the following, which you know is only ever going to throw one of 2 exceptions:
public static void ExceptionDemo(string input)
{
if (input == null)
throw new ArgumentNullException("input");
if (input.Contains(","))
throw new ArgumentException("input cannot contain the comma character");
// ...
// ... Some really impressive code here
// ...
}
A real life example of a method which does this is Membership.GetUser (String)
Which of the following would you use to call the method and handle the exceptions:
Method 1 (check the input param first first)
public static void Example1(string input)
{
// validate the input first and make sure that the exceptions could never occur
// no [try/catch] required
if (input != null && !input.Contains(","))
{
ExceptionDemo(input);
}
else
{
Console.WriteLine("input cannot be null or contain the comma character");
}
}
Method 2 (wrap the call in a try / catch)
public static void Example2(string input)
{
// try catch block with no validation of the input
try
{
ExceptionDemo(input);
}
catch (ArgumentNullException)
{
Console.WriteLine("input cannot be null");
}
catch (ArgumentException)
{
Console.WriteLine("input cannot contain the comma character");
}
}
I've had both methods taught over the years and wondered what the general best practise was for this scenario.
Update
Several posters were focusing on the method throwing the exceptions and not the way these exceptions were being handled, so I've provided an example of a .Net Framework method which behaves in the same way (Membership.GetUser (String))
So, to clarify my question, if you we're calling Membership.GetUser(input) how would you handle the possible exceptions, Method 1, 2 or something else?
Thanks
It depends, but generally, neither method presented is good. As has been said, in the first case, you are duplicating code. In the second, you are catching the exception without actually doing anything about it - not even rethrowing, just swallowing it. If you want just to log it or display some message, normally you should implement a global handler/logger using AppDomain.UnhandledException and do it there; this way, you don't have to pollute your code with unnecessary try/catch blocks.
The real question here is whether or not input being null or containing ',' is really an exceptional behavior in your specific case - e.g. if this is some GUI-entered string, then this should normally not result in an exception throw (end-user mistakes should be expected) and should be handled appropriately (e.g. with a warning to re-entry the input). In such case, using if statements to validate the input is the proper way. However, if input being null or containing ',' is an actual exceptional behavior (say, an API problem which indicates something's broken or missing) then throwing exception is ok. In this case, you can simply call ExceptionDemo(input) without try/catch. If you want to actually do something about the exception (e.g. change the input in some way), then use try/catch.
Callers should not assume anything about code they're calling.
Your first example is bad, because you're duplicating code: the caller performs almost (string.INOE() vs string == null) the same check as the callee (until either of them changes).
The second example is extremely bad as it ignores the thrown exceptions and gives its own interpretation to them.
As usual: it depends. If you have a properly layered application where the method calls are in your UI layer, you do want to just catch the exception the method throws: you'll want to display those errors to the user.
It depends on how many times ExceptionDemo is called and who it is exposed to. If it was used extensively, you wouldn't want to check the conditions before calling ExceptionDemo, when you know (and document) that ExceptionDemo does the checks anyway.
Given the return type is void, what about changing ExceptionDemo to have no effect if the input is wrong?
(Did you notice that you are stricter in Method 1 - the empty string is not a valid input, but in Method 2 it is)
I would recommend standard and generic structure as below :
public static void Operation(object input)
{
try
{
ValidateInput(input);
//Do Operation
}
catch (MySpecificException subSubExceptionType) //Catch most specific exceptions
{
//Log or process exception
throw;
}
catch (MySpecificException subExceptionType) //Catch specific exception
{
//Log or process exception
}
catch (Exception exceptionType) //Catch most generic exception
{
//Log or process exception
}
finally
{
//Release the resources
}
}
private static void ValidateInput(object input)
{
if(input == null)
throw new NoNullAllowedException();
//Check if properties of input are as expected. If not as expected then throw specific exception with specific message
}

Categories

Resources