Idiomatic way to throw exceptions to a delegate - c#

I have a monitor class which monitors a device and reports if that device successfully receives usable data. This can happen anytime.
A client creates its own monitor by passing delegates, starts it and waits for either the successfully read data or a kind of domain specific exception type (one base exception type)
What would be the idiomatic way of throwing subtypes of the base exception type and enable the client to respond to each subtype individually?
public class MyMonitor
{
private SuccessHandler _successHandler;
private ErrorHandler _errorHandler;
public delegate void SuccessHandler(MyDTO result);
public delegate void ErrorHandler(MyBaseException exception);
public MyMonitor(SuccessHandler successHandler, ErrorHandler errorHandler) {
_successHandler = successHandler;
_errorHandler = errorHandler;
}
public void start() {
try {
_successHandler(new MyDTP().doSomethingRisky());
} catch(Exception e) {
_errorHandler(e);
}
}
}
public class Client {
static void Main(string[] args) {
MyMonitor monitor = new MyMonitor(new MyMonitor.SuccessHandler(handleSuccess), new MyMonitor.ErrorHandler(handleException));
monitor.start();
}
static void handleSuccess(MyDTO result) {
// do something with result
}
static void handleException(MyBaseException e) {
try {
throw e;
} catch(UserException mbe) {
// present message to user
} catch(DataNotFoundException se) {
// log error and show generic error message
} catch(UnexpectedException ue) {
// log error and try to hide it from the user
}
}
}

So, why don't you handle the exceptions in your main instead of the monitor-class?
If that isn't an option, you have (at least) two alternatives:
static void handleException(MyBaseException e)
{
if (e is UserException)
{
// present message to user
}
else if (e is DataNotFoundException)
{
// log error and show generic error message
}
elseif (e is UnexpectedException)
{
// log error and try to hide it from the user
}
else
{
// might want to rethrow the exception, do a general handling,...
}
}
That way you don't have to rethrow the exception, just to catch it again.
But this can get ugly if you have many subtypes to handle and here is where multidispatch comes in.
static void HandleException(MyBaseException e)
{
HandleSubException((dynamic)e);
}
static void HandleSubException(MyBaseException e)
{
// might want to rethrow the exception, do a general handling,...
}
static void HandleSubException(DataNotFoundExceptione)
{
// log error and show generic error message
}
static void HandleSubException(UnexpectedException e)
{
// log error and try to hide it from the user
}
static void HandleSubException(UserExceptione)
{
// present message to user
}
Now you can tend to each exception in its own method and is much easier to read and maintain.
Having said that, I'm not entirely sure if this falls under best practice.

Related

How to make exception handler using events

I have dll Scripting. In that dll I have class named Scripter. In Scripter class i call some methods which loads data from MySQL database (LoadTables()). In that functions which loads data from MySQL database exception could happen. I want to be able, later in my app where I use Scripting.dll, to do something like this:
Scrpter sc = new Scripter();
sc.OnError += ErrorOccured;
And want to have function ErrorOcured in my app which will be like:
private void ErrorOccured(Exception exception)
{...}
What I need to have in Scripter class, and how should I pass exception in catch block in LoadTables so I could later use ErrorOcured() to see what happend wrong?
As Picoh and Zoltan commented on your question, you can easily wrap method calls to Scripter methods in try/catch block. But, if you want to use event (with custom args), you can do something like this:
//your class
public class Scripter
{
public Scripter()
{
}
//public event with custom event args
public EventHandler<ScripterErrorEventArgs> OnError;
//just for test
public void RaiseError()
{
//error which is caught here
Exception ex = new Exception("something happened");
OnError?.Invoke(this, new ScripterErrorEventArgs(ex));
}
}
//class for custom event args. add your own other properties as needed
public class ScripterErrorEventArgs : EventArgs
{
public ScripterErrorEventArgs()
{
}
public ScripterErrorEventArgs(Exception ex)
{
this.Exception = ex;
}
public Exception Exception { get; set; }
}
//usage
public void someMethod()
{
Scripter s = new Scripter();
s.OnError += new EventHandler<ScripterErrorEventArgs>(LogError)
s.RaiseError();
}
private void LogError(object sender, ScripterErrorEventArgs e)
{
//your code here
}
You could try to modify your Scripter class like this
class Scripter
{
public event EventHandler<Exception> ErrorOcurred;
protected virtual void OnErrorOcurred(Exception e)=>ErrorOcurred?.Invoke(this, e);
public void ThrowsException()
{
try
{
throw new Exception("Throws exception");
}
catch (Exception ex)
{
OnErrorOcurred(ex);
}
}
}
This way you can subscribe to ErrorOcurred and receive notifications about exceptions. You must call OnErrorOcurred in every place you catch an exception
Hope this helps

Handle exceptions in events

In this example I have two subscribers to my event. One of the subscribers raises an Exception but I would like to prevent all subscribers to fail when only one of them incurs in an Exception. The try-catch statement is not enough to capture the exception of the Dog class, it makes the Cat class fails too.
using System;
namespace EventsExample
{
class BreadWinnerEventArgs : EventArgs
{
public string Name { get; set; }
}
class BreadWinner // publisher
{
public event EventHandler<BreadWinnerEventArgs> ArrivedHome; // 2.
public void Action(BreadWinnerEventArgs args)
{
Console.WriteLine("Papa says: I'm at home!");
OnArriveHome(args);
}
protected virtual void OnArriveHome(BreadWinnerEventArgs args)
{
if (ArrivedHome != null)
{
foreach (EventHandler<BreadWinnerEventArgs> handler in ArrivedHome.GetInvocationList())
{
try
{
var t = ArrivedHome; // publisher uses sames signature as the delegate
if (t != null)
t(this, args);
}
catch (Exception e)
{
Console.WriteLine("Error in the handler {0}: {1}", handler.Method.Name, e.Message);
}
}
}
}
}
class Dog
{
public void OnArrivedHome(object source, BreadWinnerEventArgs e)
{
throw new Exception();
Console.WriteLine(String.Format("Dog says: Whoof {0}!", e.Name));
}
}
class Cat
{
public void OnArrivedHome(object source, BreadWinnerEventArgs e)
{ Console.WriteLine(String.Format("Cat hides from {0}", e.Name)); }
}
class Program
{
static void Main(string[] args)
{
BreadWinner papa = new BreadWinner(); // publisher
Dog dog = new Dog(); // subscriber
Cat cat = new Cat();
papa.ArrivedHome += dog.OnArrivedHome; // subscription
papa.ArrivedHome += cat.OnArrivedHome;
papa.Action(new BreadWinnerEventArgs() { Name = "Papa" });
Console.Read();
}
}
}
You almost had it, you just where using t where you should have been using handler, you also where using ArrivedHome where you should have been using t. I also modified the code to wrap up all the exceptions and the delegate who called them in to a custom exception then wrap those in an aggragate exception and have the code raise that.
protected virtual void OnArriveHome(BreadWinnerEventArgs args)
{
var t = ArrivedHome; // publisher uses sames signature as the delegate
if (t != null)
{
var exceptions = new List<Exception>();
foreach (EventHandler<BreadWinnerEventArgs> handler in t.GetInvocationList())
{
try
{
try
{
handler(this, args);
}
catch (Exception e)
{
Console.WriteLine("Error in the handler {0}: {1}", handler.Method.Name, e.Message);
throw new DelegateException(handler, e, this, args); //Throw the exception to capture the stack trace.
}
}
catch (DelegateException e)
{
exceptions.Add(e);
}
}
if (exceptions.Count > 0)
{
throw new AggregateException(exceptions);
}
}
}
///Elsewhere
sealed class DelegateException : Exception
{
public Delegate Handler { get; }
public object[] Args { get; }
public DelegateException(Delegate handler, Exception innerException, params object[] args) : base("A delegate raised an error when called.", innerException)
{
Handler = handler;
Args = args;
}
}
However I don't think you really should be doing this, this deviates from the "expected behavior" and may catch other programmers off guard if they have to consume your classes that do this.
I'm not saying that you should do this, but this is one way to handle it:
protected virtual void OnArriveHome(BreadWinnerEventArgs args)
{
var handler = ArrivedHome;
if (handler == null)
return;
foreach (var subscriber in handler.GetInvocationList())
{
try
{
subscriber(this, args);
}
catch (Exception ex)
{
//You can, and probably should, remove the handler from the list here
}
}
}
This allows you to invoke each one of the subscribers individually instead of as a group, and catch an exception when one of them throws. The problem I have with doing this is that you really can't know what broke, or do anything to fix it. All you can do is log and optionally remove that event handler so that the next time you don't throw on that one.
Removing the handler may also be bad practice since it can be difficult to trace why a previously assigned handler is now unassigned.

How to properly notify caller on failure of the asynchronous operation?

Class FeatureManager manages some feature and looks something like this:
public class FeatureManager
{
public event EventHandler FeatureEnabledChangedEvent;
private void OnFeatureEnabledChanged()
{
if (FeatureEnabledChangedEvent != null)
{
FeatureEnabledChangedEvent(this, EventArgs.Empty);
}
}
public event EventHandler FeatureEnableBusyChangedEvent;
private void OnFeatureEnableBusyChanged()
{
if (FeatureEnableBusyChangedEvent != null)
{
FeatureEnableBusyChangedEvent(this, EventArgs.Empty);
}
}
public event EventHandler FeatureEnableFailedEvent;
private void OnFeatureEnableFailed(FeatureEnableFailedEventArgs args)
{
if (FeatureEnableFailedEvent!= null)
{
FeatureEnableFailedEvent(this, args);
}
}
private bool _isFeatureEnabled
public bool IsFeatureEnabled
{
get
{
return _isFeatureEnabled;
}
private set
{
if (_isFeatureEnabled != value)
{
_isFeatureEnabled = value;
OnFeatureEnabledChanged();
}
}
}
private bool _isFeatureEnableBusy;
public bool IsFeatureEnableBusy
{
get
{
return _isFeatureEnableBusy;
}
private set
{
if (_isFeatureEnableBusy != value)
{
_isFeatureEnableBusy = value;
OnFeatureEnableBusyChanged();
}
}
}
public async Task EnableFeature()
{
IsFeatureEnableBusy = true;
try
{
// By its nature, process of enabling this feature is asynchronous.
await EnableFeatureImpl(); // can throw exception
IsFeatureEnabled = true;
}
catch(Exception exc)
{
OnFeatureEnableFailed(new FeatureEnableFailedEventArgs(exc.Message));
}
finally
{
IsFeatureEnableBusy = false;
}
}
}
UI class FeatureView has to be notified when:
IsFeatureEnableBusy changes (or, in other words when EnableFeature is being executed - in order to disable some controls)
IsFeatureEnabled changes
EnableFeature fails (when it throws exception in which case FeatureView displays error message
to the user)
EnableFeature can be called from some Engine class E (automatically, during the initialization on the application's launch) and also from FeatureView (when user presses 'Enable' button).
In order to satisfy requirement where FeatureView has to be notified when EnableFeature fails after it's been called by E, I added an event FeatureEnableFailedEvent.
When E calls EnableFeature and EnableFeature throws an exception, FeatureView receives FeatureEnableFailedEvent and displays error message. But when FeatureView itself calls EnableFeature and EnableFeature fails, FeatureView catches thrown exception but also gets notified on this failure from FeatureEnableFailedEvent so error handler is called twice. How to avoid this?
One solution is to declare EnableFeature as an old-style async method (and use BackgroundWorker) as in the following snippet:
public class FeatureManager
{
public void EnableFeatureAsync()
{
var bgw = new BackgroundWorker();
bgw.DoWork += (sender, e) =>
{
IsFeatureEnableBusy = true;
EnableFeatureImpl(); // can throw exception
};
bgw.RunWorkerCompleted += (sender, args) =>
{
IsFeatureEnableBusy = false;
if (args.Error == null)
{
IsFeatureEnabled = true;
}
else
{
OnFeatureEnableFailed(new FeatureEnableFailedEventArgs(args.Error.Message));
}
};
bgw.RunWorkerAsync();
}
}
In this case, caller of EnableFeatureAsync can assume that this method runs asynchronously (suffix Async in method's name should be a hint) and that it has to subscribe to FeatureEnableFailedEvent if want to be notified on the method failure. This way FeatureView gets notified on EnableFeatureAsync failure only once so error handler is called once as it should be.
Is this a good approach? Could this be achieved by still using async/await in some way? Is it good assuming that suffix Async in method's name is a good enough hint to callers so they know that this method runs as asynchronous one and that they have to look for some events to subscribe to?
As commented by #svick, I also don't see why your FeatureView catches the exception and also gets the event, when the exception is not rethrown in the handler of FeatureManager. But here is an different approach, which I'd prefer over yours based on events:
Use TaskCompletionSource to let the view know when the enablement of a feature did throw an exception even when FeatureView is not the caller of EnableFeature() (btw, by convention the method should also be named EnableFeatureAsync in the first example).
public class FeatureManager
{
public TaskCompletionSource<bool> FeatureCompleted { get; private set; }
// if you still need this property
public bool IsFeatureEnabled
{
get { return FeatureCompleted.Task.IsCompleted; }
}
public FeatureManager() {}
public async Task EnableFeature()
{
IsFeatureEnableBusy = true;
try
{
// By its nature, process of enabling this feature is asynchronous.
await EnableFeatureImpl(); // can throw exception
this.FeatureCompleted.TrySetResult(true);
}
catch(Exception exc)
{
this.FeatureCompleted.TrySetException(exc);
}
finally
{
IsFeatureEnableBusy = false;
}
}
}
Your FeatureView instance now needs to await the Task of the TaskCompletionSource. The code could look like this:
public class FeatureView
{
// if you still need this property
public async void HandleFeatureCompleted(FeatureManager fm)
{
try
{
await fm.FeatureCompleted.Task;
}
catch(Exception e)
{
// handle exception
}
}
}
You have to provide the correct FeatureManager instance to your view. I'm not sure if this approach is appropriate if you have hundredths or even thousands of FeatureManager instances messages. I'd be happy if more somebody of the commenters could provide feedback about this.

C# transmit exception from class to Form

How can I pass an error in the form of the class. When creating exemplar class.
Class:
class ThreadSafeLog
{
public ThreadSafeLog()
{
try
{
if (!File.Exists(Path_))
{
using (File.Create(Path_)) { }
}
}
catch(Exception e)
{
MessageBox.Show(e.Message);//error transmit exception to Form
}
}
}
Form:
ThreadSafeLog log = new ThreadSafeLog(
#"R:\project\ThreadSafeLog\ThreadSafeLogTest\ThreadSafeLogTest\bin\");
Why would ThreadSafeLog be concerned with what the form that uses it is doing? How is it even supposed to know it's being used by a form? Catch the exception in the form instead.
class ThreadSafeLog
{
public ThreadSafeLog()
{
if (!File.Exists(Path_))
{
using (File.Create(Path_)) { }
}
}
}
Form:
try
{
ThreadSafeLog log = new ThreadSafeLog(
#"R:\project\ThreadSafeLog\ThreadSafeLogTest\ThreadSafeLogTest\bin\");
}
catch (Exception ex)
{
MessageBox.Show(e.Message);
}
Though, as Adriano points out, you may also want to question why you're displaying internal error details directly to users.
I would not notify log's users about an error inside the logger itself. Log is, usually, a subsystem that it's good to consider error-safe and thread-safe or you'll make your code a messy. Imagine:
try
{
// Do something that may fail
}
catch (SomeException e)
{
try
{
logger.Log(e);
}
catch (IOException)
{
// Ooops, log doesn't work. What should I do?
// Should I display a MessageBox?
}
}
Your code will become quickly a messy (imagine to repeat the same check again and again for each trace log).
What I would do is to ignore (if possible) errors inside log function and to take another action instead. How? Just add an event to your log class (code is an example not for production!):
class Logger
{
public event EventHandler<LogErrorEventArgs> Error;
public void Log(Exception e)
{
try
{
// Try to write log somewhere...
}
catch (IOException internalException)
{
EventHandler<LogErrorEventArgs> error = Error;
if (error != null)
error(this, new LogErrorEventArgs(e, internalException);
}
}
}
In this way unhandled errors inside logger will go unnoticed (if this is applicable) or they may be handled (in the way it's appropriate, if there is a way). For example:
Logger logger = new Logger();
logger.Error += delegate(object sender, LogErrorEventArgs e)
{
if (SystemInformation.UserInteractive)
MessageBox.Show(e.ExceptionToLog.Message);
};
You may attach multiple handlers too (for example to log to Windows Log and notify user is session is interactive). When you use log you won't be aware it may fail (so you won't have to fill your code with try/catch everywhere) but if needed you may still be notified and errors inside log will be handled in a consistent manner.
Now let's rewrite the code in our first example like this:
try
{
// Do something that may fail
}
catch (SomeException e)
{
logger.Log(e);
}
Straight, right? I assumed logger will always write an exception (somehow) but it's easy to change code to handle a raw String instead. I omitted code for LogErrorEventArgs because pretty obvious anyway it may look like this:
class LogErrorEventArgs : EventArgs
{
public LogErrorEventArgs(
Exception exceptionToLog,
Exception internalLoggerException)
{
ExceptionToLog = exceptionToLog;
InternalLoggerException = internalLoggerException;
}
public Exception ExceptionToLog
{
get;
set;
}
public Exception InternalLoggerException
{
get;
set;
}
}

ServiceBase.Run, why can't I catch it's exceptions, or react to them in some other way?

I'm calling the following from my entry point static main method:
try { ServiceBase.Run(new MonitorSer()); }
catch (Exception ex) { Console.WriteLine(ex.Message + Process.GetCurrentProcess().MainModule.FileName); }
MonitorSer is an instance of:
class MonitorSer : ServiceBase {
and the entry main method is a member of my class:
[RunInstaller(true)]
public class WindowsServiceInstaller : Installer {
I've had good results catching exceptions for debugging but sometimes they seem to find their own way around my traps, as in this case.
I get a windows box flash up telling me I need to install using installutil when what I really want is to find the name of this process and call it again with the -i switch which I have wired up to make it install intself (credit to those here who contributed/recycled that code).
What makes this more frustrating is that if I set breakpoints upto (or on) the call to ServiceBase.Run, it will fail silently and I am left with the blinking console.
UPDATE
static void Install(bool undo, string[] args)
{
try
{
Console.WriteLine(undo ? "uninstalling" : "installing");
using (AssemblyInstaller inst = new AssemblyInstaller(typeof(MonitorSer).Assembly, args))
{
IDictionary state = new Hashtable();
inst.UseNewContext = true;
try
{
if (undo) inst.Uninstall(state);
else
{
inst.Install(state);
inst.Commit(state);
}
}
catch
{
try
{
inst.Rollback(state);
}
catch { }
throw;
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
}
}
I clumped the entry point here so I could call the above function, I'll try moving that to another class and setting the entry point there, but I know I can make this entry point (that you, Dmitry, deny) work by calling itself with the appropriate argument to install- which only the BaseService class can do- correct me if I am wrong.
[RunInstaller(true)]
public class WindowsServiceInstaller : Installer
Is not your entry point. This will get called once when you install your service using InstallUtil.exe. Entry point can be specified in the project properties and it usually defaults to Program.Main. You should not be starting your service from Installer class.
CLR will let you know of unhandled exceptions if you subscribe to this event:
static void Main() {
...
AppDomain.CurrentDomain.UnhandledException
+= CurrentDomain_UnhandledException;
...
}
private static void CurrentDomain_UnhandledException(
Object sender,
UnhandledExceptionEventArgs e) {
if (e != null && e.ExceptionObject != null) {
// log exception:
}
}
This event provides notification of uncaught exceptions. It allows the
application to log information about the exception before the system
default handler reports the exception to the user and terminates the application
...
Starting with the .NET Framework version 4, this event is not raised
for exceptions that corrupt the state of the process, such as stack
overflows or access violations, unless the event handler is
security-critical and has the
HandleProcessCorruptedStateExceptionsAttribute attribute.
application.
Another place where you might want to log exceptions in windows service (because .NET/SCM will swallow startup exceptions):
protected override void OnStart(String[] args) {
try {
} catch(Exception e) {
// log exception:
throw;
}
}

Categories

Resources