通过类库中的事件引发异常
通过订阅包含异常的事件,为消费者提供处理异常的选项是一种不良做法吗?如果是这样,如果我这样做,我会遇到什么问题?下面的代码是一个例子。通过类库中的事件引发异常
我设想使用这种模式与视图模型。视图模型将订阅ExceptionRaised事件并将异常传递给日志记录类。我不想将日志记录类注入到StatisticsCalculations类中。
public class StatisticalCalculations
{
public event EventHandler<Exception> ExceptionReceived;
public double GetStandardDeviation(IEnumerable<double> values)
{
double standardDeviation = double.NaN;
try
{
double average = values.Average();
double sum = values.Sum(value => Math.Pow(value - average, 2));
standardDeviation = Math.Sqrt((sum)/(values.Count() - 1));
}
catch (Exception ex)
{
ExceptionReceived?.Invoke(this, ex);
}
return standardDeviation;
}
}
使用类似这样的事件将异常从您调用的实际方法调用中断开。我认为最好为此使用Exception monad。
你的类应该是这样的:那么
public class StatisticalCalculations
{
public Exceptional<double> GetStandardDeviation(IEnumerable<double> values)
{
try
{
double standardDeviation = double.NaN;
//Perform calculation
return standardDeviation.ToExceptional();
}
catch (Exception ex)
{
return new Exceptional<double>(ex);
}
}
}
您的通话功能,可解压Exceptional<double>
,看它是否有一个值,或者如果它得到了一个错误。
你需要支持这样做的类:
public class Exceptional<T>
{
public bool HasException { get; private set; }
public Exception Exception { get; private set; }
public T Value { get; private set; }
public Exceptional(T value)
{
HasException = false;
Value = value;
}
public Exceptional(Exception exception)
{
HasException = true;
Exception = exception;
}
public Exceptional(Func<T> func)
{
try
{
this.Value = func();
this.HasException = false;
}
catch (Exception exc)
{
this.Exception = exc;
this.HasException = true;
}
}
public override string ToString()
{
return this.HasException
? Exception.GetType().Name
: (this.Value != null
? this.Value.ToString()
: "null");
}
}
public static class ExceptionalEx
{
public static Exceptional<T> ToExceptional<T>(this T value)
{
return new Exceptional<T>(value);
}
public static Exceptional<T> ToExceptional<T>(this Func<T> func)
{
return new Exceptional<T>(func);
}
public static Exceptional<U> SelectMany<T, U>(this Exceptional<T> value, Func<T, Exceptional<U>> k)
{
return (value.HasException)
? new Exceptional<U>(value.Exception)
: k(value.Value);
}
public static Exceptional<V> SelectMany<T, U, V>(this Exceptional<T> value, Func<T, Exceptional<U>> k, Func<T, U, V> m)
{
return value.SelectMany(t => k(t).SelectMany(u => new Exceptional<V>(() => m(t, u))));
}
}
这是如何比允许例外提高更好? –
@DavidHeffernan - 它比提高事件来暴露异常要好,因为它保持对方法的调用和产生的异常紧密耦合。试图将调用与提出的事件进行匹配将会产生问题。此外,这允许组合,提高事件不允许。 – Enigmativity
否。最好是提出异常并让消费者决定是否以及何时处理它。如果消费者想要在调用堆栈的顶部处理这个问题,那么他们必须在那里编组。 –
_“这是一个不好的做法......” _ - 舆论彻底的问题,因而不适合堆栈溢出。也就是说,我没有看到好处;视图模型本身很容易实现某种集中式异常报告,并且可以以更合适的方式来实现。你的例子捕获_all_异常,这是一个绝对不好的主意。但是与此同时,只有调用者真正知道它能够合理地处理什么,以及应该怎样强制该过程终止。 –
我不同意彼得。有一个好处。将异常消息转储到记录器并保存到文件中没有区别;或者将一个异常添加到可枚举对象中。使用你的代码没有问题。真正的问题是找到例外的根源。事件是异步的,所以只要将错误消息添加到记录器中就不会指示发生错误的地方。因此添加堆栈跟踪将提供更多信息。 – jdweng
而不是订阅一个事件,为什么你不能只处理来自调用代码的异常?这可能是一个应用程序范围的包装器(ASP.NET MVC是如何工作的)或包装调用的调用者特定逻辑。 – AndrewP