在这个例子中,我有两个订阅我的活动。其中一个订阅者引发了一个Exception,但我想阻止所有订阅者在其中只有一个发生异常时失败。 try-catch语句不足以捕获Dog类的异常,它也使Cat类失败。
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();
}
}
}
答案 0 :(得分:2)
你几乎拥有它,你只是使用t
你应该使用handler
的地方,你也可以使用ArrivedHome
你应该使用t
的地方。我还修改了代码以包装所有异常以及将它们调用到自定义异常的委托,然后将它们包装在aggragate异常中并使代码提高。
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;
}
}
但是,我不认为你真的应该这样做,这会偏离预期的行为&#34;如果他们必须消耗你的课程,可能会让其他程序员措手不及。
答案 1 :(得分:1)
我并不是说你应该这样做,但这是处理它的一种方法:
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
}
}
}
这允许您单独调用每个订阅者而不是作为一个组,并在其中一个订阅者抛出时捕获异常。我这样做的问题是你真的不知道什么破坏了,或者做了什么来解决它。您所能做的就是记录并选择性地删除该事件处理程序,以便下次您不再使用该事件处理程序。
删除处理程序也可能是不好的做法,因为很难找到为什么以前分配的处理程序现在未分配的原因。