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.
Related
I am trying to better understand an example from the C# Programming Guide documentation. I have watched it execute step-by-step in Visual Studio, and read the documentation for clues.
It uses a generic EventHandler delegate. Here is the code:
using System;
using System.Collections.Generic;
namespace DotNetEvents
{
class Program
{
class CustomEventArgs : EventArgs
{
public CustomEventArgs(string s)
{
message = s;
}
private string message;
public string Message
{
get { return message; }
set { message = value; }
}
}
class Publisher
{
public event EventHandler<CustomEventArgs> RaiseCustomEvent;
public void DoSomething()
{
Console.WriteLine("Did something");
}
protected virtual void OnRaiseCustomEvent(CustomEventArgs e)
{
EventHandler<CustomEventArgs> handler = RaiseCustomEvent;
if(handler != null)
{
e.Message += $" at {DateTime.Now}";
handler(this, e);
}
}
}
class Subscriber
{
private string id;
public Subscriber(string ID, Publisher pub)
{
id = ID;
pub.RaiseCustomEvent += HandleCustomEvent;
}
void HandleCustomEvent(object sender, CustomEventArgs e)
{
Console.WriteLine(id + " received this message: {0}", e.Message);
}
}
static void Main(string[] args)
{
Publisher pub = new Publisher();
Subscriber sub1 = new Subscriber("sub1", pub);
Subscriber sub2 = new Subscriber("sub2", pub);
pub.DoSomething();
Console.ReadKey();
}
}
}
Why does it seem like the following line is completely arbitrary:
protected virtual void OnRaiseCustomEvent(CustomEventArgs e)
{
EventHandler<CustomEventArgs> handler = RaiseCustomEvent;
if(handler != null)
{
e.Message += $" at {DateTime.Now}";
handler(this, e);
}
}
Nothing in this method affects the outcome if commented out. Additionally, I commented out these lines & it didn't seem to affect anything:
//pub.RaiseCustomEvent += HandleCustomEvent;
and,
/* void HandleCustomEvent(object sender, CustomEventArgs e)
{
Console.WriteLine(id + " received this message: {0}", e.Message);
} */
It would help a lot to understand why this stuff is added in. Thanks. For reference, here is the page I got the code from. I removed some of its comments since I was typing it rather than copying & pasting. https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-publish-events-that-conform-to-net-framework-guidelines
When you call pub.RaiseCustomEvent += HandleCustomEvent this code in itself does absoluetely nothing except to register a method to an event. When you don´t ever raise that event, that method (we call it an event-handler) will surely never be executed.
In your code you would raise that event by calling OnRaiseCustomEvent somewhere in your Publisher-class. However we can´t know when that event should actually be raised.
For example you could raise the event whenever some client called DoSomething on any Publisher-instance. Then you would call OnRaiseCustomEvent from within your DoSomething-method:
public void DoSomething()
{
OnRaiseCustomEvent(new CustomEventArgs("I did something"));
Console.WriteLine("Did something");
}
This when you call pub.DoSomething the event is raised and your event-handler (HandleCustomEvent) would be called.
class Program
{
static void Main(string[] args)
{
try
{
using (var ss = new extest()) {
throw new Exception("Exception1");
}
}
catch(Exception ex)
{
System.Console.WriteLine(ex.Message);
}
}
}
class extest : IDisposable
{
public void Dispose()
{
throw new Exception("Exception2");
}
}
Run the codes result is "Exception2",
So I want to know how you can catch two exceptions, or just catch an Exception1.
My project has thousands of such using, which does not add try, but extest's Dispose is only one place, and I hope to know what exception has thrown before the Dispose.
Thanks
The problem in your example is that the second exception is thrown while the first exception is being handled. I.e. the using statement is effectively a try/finally pair, with the call to Dispose() in the finally block. So, the second exception supersedes the first one.
Having a Dispose() method that throws an exception is a very bad idea. So, the best solution here is to fix that. Don't throw an exception from a Dispose() method. But if you can't fix that for some reason and you want to see both, you need to make sure you're in a position to catch both. You can do this by adding another try/catch inside the using:
try
{
using (var ss = new extest()) {
try
{
throw new Exception("Exception1");
}
catch (Exception exInner)
{
System.Console.WriteLine(ex.Message);
throw;
}
}
}
catch(Exception ex)
{
System.Console.WriteLine(ex.Message);
}
The easiest way to handle this would be to rearrange your code:
static void Main(string[] args)
{
try
{
using (var ss = new extest())
{
try
{
CodeThatMightThrowAnException();
}
catch (Exception e)
{
// Process Exception here
}
}
}
catch(Exception ex)
{
System.Console.WriteLine(ex.Message);
}
}
Edit:
If the handling of the exceptions inside the using is always going to be the same, you could build a helper class that could make refactoring easier:
public class TryCatchHelper
{
public Exception Exception { get; private set; } = null;
public void Execute(Action action)
{
try
{
action()
}
catch (Exception e)
{
exception = e;
}
}
}
Then in your method:
static void Main(string[] args)
{
var helper = new TryCatchHelper();
try
{
using (var ss = new extest())
{
helper.Execute(() => {
// Your Code Block Here
});
}
}
catch(Exception ex)
{
// The Dispose threw an exception
}
if (helper.Exception != null)
{
// Handle the exception from the block here.
}
}
it's impossible to catch more than 1 exception.
when you throw Exception2 it should be catched in your catch clause. when you see "Exception2" it is printed by System.Console.WriteLine(ex.Message);. So, you can change the log in catch, or change the throwing exception message in Dispose.
reference added:
class Program
{
static void Main(string[] args)
{
try
{
using (var ss = new extest()) {
...
}
}
catch(Exception ex)
{
System.Console.WriteLine("extest error : " + ex.Message);
}
}
}
class extest : IDisposable
{
public void Dispose()
{
throw new Exception("Dispose failed: reason");
}
}
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
I find myself using async fire-and-forget methods using void as the return value, but DO care about exceptions.
It seems to be the consensus that exceptions cannot be handled properly with async-await if no reference is hold to the executing Task and void should be.. well.. avoided..
What am I missing in the following code that apparently seems to do the job:
class Program
{
static void Main()
{
var p = new Processor();
p.ExceptionThrown += p_ExceptionThrown;
for (var i = 0; i < 10; i++)
p.ProcessAsync(i);
Console.ReadKey();
}
static void p_ExceptionThrown(object sender, Exception e)
{
Console.WriteLine("Exception caught in Main : " + e);
}
}
class Processor
{
public async void ProcessAsync(int iteration)
{
try
{
await Task.Run(() => Process(iteration));
}
catch (Exception e)
{
OnException(e);
}
}
public void Process(int iteration)
{
Thread.Sleep(500);
if(iteration == 5)
throw new Exception("AUUCH");
}
public event EventHandler<Exception> ExceptionThrown;
void OnException(Exception e)
{
var handler = ExceptionThrown;
if (handler != null)
handler(this, e);
}
}
Under the covers the async / await keywords actually generate a state-machine when they are compiled down to IL, please read about it here. The only time that you should ever use async void is on an event handler, as explained here. The issue is that when the state machine is built-out it uses the Task or Task<T> classes as the return type in order to manage the next state of the next asynchronous operation in the chain. However, when you define the method as void, it basically returns null to the state machine and then everything gets out of whack.
Exceptions from an async void can’t be caught with catch
The quote from above is from the best practices article I pointed you to before. The below alteration does work, as I have tested it to verify that it does.
class Program
{
static void Main()
{
var p = new Processor();
p.ExceptionThrown += p_ExceptionThrown;
for (var i = 0; i < 10; i++)
p.ProcessAsync(i);
Console.ReadKey();
}
static void p_ExceptionThrown(object sender, Exception e)
{
Console.WriteLine("Exception caught in Main : " + e);
}
}
class Processor
{
public async Task ProcessAsync(int iteration)
{
try
{
await Task.Run(() => Process(iteration));
}
catch (Exception e)
{
OnException(e);
}
}
public void Process(int iteration)
{
Thread.Sleep(500);
if(iteration == 5)
throw new Exception("AUUCH");
}
public event EventHandler<Exception> ExceptionThrown;
void OnException(Exception e)
{
var handler = ExceptionThrown;
if (handler != null)
handler(this, e);
}
}
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.