I would like to pass multiple error messages to the GUI. How do I have to do this? Please have a short look at my abstract example above:
try
{
LogIn(usr, pwd); //entry point
}
catch(Exception ex)
{
throw new Exception("Login failed.");
}
public void LogIn(string usr, string pwd) {
if(usr == "") {
throw new Exception("Username was empty.");
}
if(pwd== "") {
throw new Exception("Password was empty.");
}
try
{
//do some other stuff without a more specific error message
}
catch
{
throw;
}
}
Later I would like to get a result error message like
Login failed. Password was empty.
if the user didn't type in a password. Right now I just get the last error message ("login failed.") on top and so just half of my information that I would like to give to the user.
You can nest exceptions:
try
{
LogIn(usr, pwd); //entry point
}
catch(Exception ex)
{
throw new Exception("Login failed.", ex);
}
Note the second argument, and the InnerException property of Exception.
But before doing do, consider whether the above block is adding any value. If you just let the Password was empty exception escape instead, the caller would still know, generically, that the login has failed, and that exception alone seems to contain all the required information.
Only catch an exception if you have something useful to do - if you can recover an error condition, add information or do not want to expose implementation details to your callers. Otherwise, let the exceptions rise to a level where something useful can be done.
I would rethink your structure.
As in the comments pointed out there are some things to consider:
Will the method called elsewhere and could so lead to wrong usage?
If I use exceptions in my flow control, could it lead to unreadable code? (Using exceptions for flow control)
Approach with List<string> for collecting issues:
public void LogIn(string usr, string pwd)
{
List<string> errors = new List<string>();
if(string.IsNullOrEmpty(usr))
{
errors.Add("Username is empty.");
}
if(string.IsNullOrEmpty(pwd))
{
errors.Add("Password is empty.");
}
if(errors.Count > 0) // If errors occur, throw exception.
{
throw new Exception(string.Join("\r\n",errors));
}
}
You could just use ex.Message which would either be Password was empty. or Username was empty.
Related
The Best practices for exceptions document on MSDN says that you can have an exception builder method inside your class if the same exception is to be used in many parts of the class. But also, it says that in some cases, it's better to use the exception's constructor.
Let's say I have the following code in an UserData class:
private MailAddress _addr;
public UserData(string emailAddress)
{
// Tries to validate the e-mail address
try
{
_addr = new MailAddress(emailAddress);
}
catch
{
throw new ArgumentException(nameof(emailAddress), "Invalid email address.");
}
if (_addr.Address != emailAddress)
{
throw new ArgumentException(nameof(emailAddress), "Invalid email address.");
}
}
You can see that in both throw statements, I'm throwing the exact same exception.
The question is: Is it correct to add an exception builder method to get my exception and throw that? Will I get the correct stacktrace and such if I do so? And if not, how do I determine between exception builders and constructors?
Is it correct to add an exception builder method to get my exception and throw that
That depends. As suggested in the article you linked: If it's the same exception (with the same information), it makes sense to create such a helper method to keep your code clean.
Will I get the correct stacktrace and such if I do so
Yes, you will.
Take a look at this example. (DotNetFiddle).
public static void Main()
{
try{
throw CreateEx("Hi");
} catch(Exception ex) {
Console.WriteLine(ex.ToString());
}
try {
CreateEx2("Hi");
} catch(Exception ex) {
Console.WriteLine(ex.ToString());
}
}
public static Exception CreateEx(string text){
text += " Additional text";
return new ArgumentOutOfRangeException(text);
}
public static void CreateEx2(string text){
text += " Additional text";
throw new ArgumentOutOfRangeException(text);
}
The stacktrace depends on where the exception is thrown, not where it is built.
System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: Hi Additional text
at Program.Main() in d:\Windows\Temp\b4ln3dbq.0.cs:line 13
System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: Hi Additional text
at Program.CreateEx2(String text) in d:\Windows\Temp\b4ln3dbq.0.cs:line 34
at Program.Main() in d:\Windows\Temp\b4ln3dbq.0.cs:line 19
I'm making a Tic-Tac-Toe game for an assignment and I am new to C#. I have a custom exception for bad moves called BadMoveException, which would be if the user enters anything other than 0-8. There is existing code for the assignment and I'm wondering if I should do away with the code to create my own to use this exception or if it is easy enough to implement here? Here is the code:
string input;
int position;
do
{
input = Console.ReadLine();
}
while (!int.TryParse(input, out position));
I need to catch the BadMoveException, and any others with an unknown error message. Thank you in advance!
As long as your BadMoveException inherits from Exception, then you can use it just like any other Exception, like this:
try {
//do stuff
if (badMove) {
throw new BadMoveException();
}
} catch (BadMoveException) {
//user made a bad move!!
} catch {
//something else went wrong
}
There is more information about exception handling here: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/exceptions/
Here's what I have:
1- First, your exception need to inherit from Exception like this:
public class BadMoveException : Exception { // Your code here }
2- When you have an error, you use it like this:
throw new BadMoveException(// Enter parameter if you have any in you class);
And you catch it:
try
{
if(Position < 0 || Position > 8)
{
throw new BadMoveException(// Enter parameter here if you have any);
}
else
{
// Your code here
}
}
catch(BadMoveException bmex) { // Show message here }
catch(Exception ex) { // Show other exception }
Hope it helps !
Links for documentation: http://www.tutorialsteacher.com/csharp/custom-exception-csharp
https://stackify.com/csharp-exception-handling-best-practices/
I have the following situation in code, whats the best way to manage it, the comments contains the situations, and please recommend the best practice.
try
{
string errorMessage = AccountClient.GetAccount(id, out accountDetails);
// FIRST WAY : REMOVE THIS NULL CHECK AT ALL AND LEAVE GetAccountDetails to control
// the Null situation?
if (accountDetails == null)
{
// Second Way: This way? Throw exception here?
throw new ArgumentNullException(nameof(accountDetails));
//Third way? break the function?
break;
}
// GetAccount Details already has null control
Subscription subscription = AccountProcessor.GetAccountDetails(accountDetails);
}
catch (Exception e)
{
throw;
}
First of all, the costruction
catch (Exception e) {
throw;
}
is redundant one and can be eliminated. Now about nulls. There're two
cases:
null is an erroneous value and so it should be signalled
null is an expected, ordinary value and thus it should be proceeded
And so you have (null is an error)
string errorMessage = AccountClient.GetAccount(id, out accountDetails);
// What's wrong: it's id which doesn't correspond to any detail
// (we expect id being s.t. AccountClient.GetAccount(id...) returns not null detail)
if (accountDetails == null)
throw new ArgumentException($"Incorrect id {id} which doesn't have any detail.",
nameof(id));
Subscription subscription = AccountProcessor.GetAccountDetails(accountDetails);
Or (null is an expected outcome)
string errorMessage = AccountClient.GetAccount(id, out accountDetails);
if (accountDetails == null)
return null; // or any reasonable value, or just return, or create new Subscription
Subscription subscription = AccountProcessor.GetAccountDetails(accountDetails);
If you can do anything about null input then handle it.
try
{
string errorMessage = AccountClient.GetAccount(id, out accountDetails);
if (accountDetails == null)
{
// do something about it. Maybe write some logs, substitute with a default value
// or throw appropriate exception ...
}
Subscription subscription = AccountProcessor.GetAccountDetails(accountDetails);
}
catch (Exception e)
{
throw;
}
if you can't then let GetAccountDetails decide what should happen.
try
{
string errorMessage = AccountClient.GetAccount(id, out accountDetails);
Subscription subscription = AccountProcessor.GetAccountDetails(accountDetails);
}
catch (Exception e)
{
throw;
}
Also there is no need to catch an exception, doing nothing and then throw it so you can remove the whole try catch block.
It depends on where this ID is coming from. If the user typed the ID, then I wouldn't generate an Exception, since it is not a error in your program. Just treat the user input and show a proper message. Exceptions are costly, so I usually use them only when i have a real programa failure. Besides that, if you write a custom Exception Handler, it wouldn`t make sense to log a error caused by wrong user input. So i would make it like this:
if (AccountClient.AccountExists(id))
{
AccountDetails details = AccountClient.GetAccount(id);
Subscription subscription = AccountProcessor.GetAccountDetails(accountDetails);
}
Anyway, its good to treat the input on the same way, even if you had treated like above, in case there is any other non treated call to it:
public AccountDetails GetAccount(int id)
{
if (Exists(id))
GetTheAccount(id);
else
throw new Exception(String.Format("Account {0} doesn't exists", id));
}
In this case I would use an Exception because it could really represent an error, if the caller function is passing a wrong value, for instance.
I have caught an exception and after catching it I have to append the method name so that I should know which method the error came from, and then throw it to another function and save it in database.
try
{
}
catch (Exception ex)
{
string strError = ex.Message.ToString() + "methodname:getNoOfRecordsForBatchProcess";
throw strError.ToString();
}
but it gives me error that you can't use string variable to throw exception.the throw exception only use with system exception. is there any way to handle this error.
The method name is visible in Exception.StackTrace property too.
By the way you may rely on some other way to recover its name using StackFrame Class, like for example:
private static string GetCallingMethodName()
{
const int iCallDeepness = 2; //DEEPNESS VALUE, MAY CHANGE IT BASED ON YOUR NEEDS
System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace(false);
System.Diagnostics.StackFrame sframe = stack.GetFrame(iCallDeepness);
return sframe.GetMethod().Name;
}
An answer to your question:
throw new Exception(strError);
(However, as others have said, this might not be the best way to handle this.)
Possible duplicate of How to get the name of the method that caused the exception.
catch (Exception ex)
{
MethodBase site = ex.TargetSite;
string methodName = site == null ? null : site.Name;
...
}
When catching and rethrowing an exception it's 'recommended' to create a new Exception, with a meaningful error message, and pass the original exception in as the inner exception.
For example:
catch(Exception ex)
{
const string message = "Some error message.";
throw new MeaningfulException(message, ex);
}
I would also go for the StackFrame. I'm posting an extension to #Tigran's answer (because you asked for a bit more clarified usage inside the try{...}catch{...} block), so if this is helping you to understand the usage, please accept his answer, not mine:
try
{
int a = 0;
var r = 1 / a;
}
catch (Exception ex)
{
throw new Exception(
String.Format("{0} Method name: {1}",
ex.Message,
GetCallingMethodName()),
ex);
}
The GetCallingMethodName:
private static string GetCallingMethodName()
{
const int iCallDeepness = 1; //DEEPNESS VALUE, MAY CHANGE IT BASED ON YOUR NEEDS
System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace(false);
System.Diagnostics.StackFrame sframe = stack.GetFrame(iCallDeepness);
return sframe.GetMethod().Name;
}
P.S. #Tigran, I will remove this answer if you consider that it's not needed.
I am wondering can try..catch force execution to go into the catch and run code in there?
here example code:
try {
if (AnyConditionTrue) {
// run some code
}
else {
// go catch
}
} catch (Exception) {
// run some code here...
}
try{
if (AnyConditionTrue){
//run some code
}
else{
throw new Exception();
}
}
catch(){
//run some code here...
}
But like Yuck has stated, I wouldn't recommend this. You should take a step back at your design and what you're looking to accomplish. There's a better way to do it (i.e. with normal conditional flow, instead of exception handling).
Rather than throwing an Exception in the else, I would recommend extracting the code from your catch into a method and call that from your else
try
{
if (AnyConditionTrue)
{
MethodWhenTrue();
}
else
{
HandleError();
}
}
catch(Exception ex)
{
HandleError();
}
Yes, you have to throw exception :
try
{
throw new Exception("hello");
}
catch (Exception)
{
//run some code here...
}
An effective way to throw an Exception and also jump to Catch as so:
try
{
throw new Exception("Exception Message");
}
catch (Exception e)
{
// after the throw, you will land here
}
if(conditiontrue)
{
}
else{
throw new Exception();
}
Yes, if you throw the exception that you intend to catch from within the try, it will be caught in the catch section.
I have to ask you why you would want to do this though? Exception handling is not meant to be a substitute for control flow.
I think what you want is a finally block: http://msdn.microsoft.com/en-us/library/zwc8s4fz(v=vs.80).aspx
see this
try
{
doSomething();
}
catch
{
catchSomething();
throw an error
}
finally
{
alwaysDoThis();
}
This is different if/when you do this:
try
{
doSomething();
}
catch
{
catchSomething();
throw an error
}
alwaysDoThis();// will not run on error (in the catch) condition
the the this last instance, if an error occurs, the catch will execute but NOT the alwaysDoThis();. Of course you can still have multiple catch as always.
As cadrel said, but pass through an Exception to provide more feedback, which will be shown in the innerException:
try
{
if (AnyConditionTrue)
{
MethodWhenTrue();
}
else
{
HandleError(new Exception("AnyCondition is not true"));
}
}
catch (Exception ex)
{
HandleError(ex);
}
...
private void HandleError(Exception ex) {
throw new ApplicationException("Failure!", ex);
}
public class CustomException: Exception
{
public CustomException(string message)
: base(message) { }
}
//
if(something == anything)
{
throw new CustomException(" custom text message");
}
you can try this
You could throw an exception to force a catch
throw new Exception(...);
why are you catching an exception? Why not just run the code in your "else" block? If you MUST do it that way, just throw a new exception
throw new Exception();
Slight resurrection, but I wanted to add both a sample (primarily like others) and a use case.
public int GetValueNum(string name)
{
int _ret = 0;
try
{
Control c = (extendedControls.Single(s => s.ValueName == name) as Control);
if (c.GetType() == typeof(ExtendedNumericUpDown))
_ret = (int)((ExtendedNumericUpDown)c).Value;
else
throw new Exception();
}
catch
{
throw new InvalidCastException(String.Format("Invalid cast fetching .Value value for {0}.\nExtendedControllerListener.GetValueNum()", name));
}
return _ret;
}
In my case, I have custom controls - a handful of controls that use a base Windows.Forms control, but add two bools and a string for tracking, and also automatically get registered to a Singleton List<T> so they can be properly fetched without drilling down through control containers (it's a tabbed form).
In this case, I'm creating some methods to easily get values (.Value, .Text, .Checked, .Enabled) by a name string. In the case of .Value, not all Control objects have it. If the extended control is not of type ExtendedNumericUpDown, it IS an InvalidCastException as the method should not be called against that type of control. This isn't flow, but the prescribed usage of invalid cast. Since Control doesn't naturally have a .Value property, Visual Studio won't let me just force an attempt and fail after.