2017-09-06 26 views
-1

通过订阅包含异常的事件,为消费者提供处理异常的选项是一种不良做法吗?如果是这样,如果我这样做,我会遇到什么问题?下面的代码是一个例子。通过类库中的事件引发异常

我设想使用这种模式与视图模型。视图模型将订阅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; 
    } 
} 
+0

_“这是一个不好的做法......” _ - 舆论彻底的问题,因而不适合堆栈溢出。也就是说,我没有看到好处;视图模型本身很容易实现某种集中式异常报告,并且可以以更合适的方式来实现。你的例子捕获_all_异常,这是一个绝对不好的主意。但是与此同时,只有调用者真正知道它能够合理地处理什么,以及应该怎样强制该过程终止。 –

+0

我不同意彼得。有一个好处。将异常消息转储到记录器并保存到文件中没有区别;或者将一个异常添加到可枚举对象中。使用你的代码没有问题。真正的问题是找到例外的根源。事件是异步的,所以只要将错误消息添加到记录器中就不会指示发生错误的地方。因此添加堆栈跟踪将提供更多信息。 – jdweng

+0

而不是订阅一个事件,为什么你不能只处理来自调用代码的异常?这可能是一个应用程序范围的包装器(ASP.NET MVC是如何工作的)或包装调用的调用者特定逻辑。 – AndrewP

回答

-1

使用类似这样的事件将异常从您调用的实际方法调用中断开。我认为最好为此使用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)))); 
    } 
} 
+0

这是如何比允许例外提高更好? –

+0

@DavidHeffernan - 它比提高事件来暴露异常要好,因为它保持对方法的调用和产生的异常紧密耦合。试图将调用与提出的事件进行匹配将会产生问题。此外,这允许组合,提高事件不允许。 – Enigmativity

+0

否。最好是提出异常并让消费者决定是否以及何时处理它。如果消费者想要在调用堆栈的顶部处理这个问题,那么他们必须在那里编组。 –