C# 如何设计流畅的界面(用于异常处理)?

C# 如何设计流畅的界面(用于异常处理)?,c#,dsl,fluent-interface,C#,Dsl,Fluent Interface,我正在回顾代码库的一部分,接下来是异常处理部分,这部分非常混乱。我想换件更优雅的。然后我想,如果我有一个流畅的界面,可以帮助我注册一些例外列表的策略,并让一个例外处理经理为我做其余的事情,这可能不是一个坏主意: 下面是一个它应该如何工作的示例: For<TException>.RegisterPolicy<TPolicy>(a lambda expression that describes the detail); 披头士1692,请原谅,我将从解决重构异常处理的主要

我正在回顾代码库的一部分,接下来是异常处理部分,这部分非常混乱。我想换件更优雅的。然后我想,如果我有一个流畅的界面,可以帮助我注册一些例外列表的策略,并让一个例外处理经理为我做其余的事情,这可能不是一个坏主意:

下面是一个它应该如何工作的示例:

For<TException>.RegisterPolicy<TPolicy>(a lambda expression that describes the detail);

披头士1692,请原谅,我将从解决重构异常处理的主要问题开始,而不是直接跳到DSL部分

在你的问题中,你说:

我正在回顾代码库的一部分,接下来是异常处理部分,这部分非常混乱。我想换件更优雅的

我想这是你最关心的。因此,我将尝试为您提供一个如何使其更加优雅的指南-实际上,您提供的代码片段不优雅并不是关于它不是DSL或流畅的接口,而是关于设计质量。如果你的设计中有冗余和耦合,那么在冗余和耦合之上创建一个流畅的界面只会让它变得更糟糕

答案很长,我会参考一些代码质量,所以如果您需要进一步的解释,请告诉我。因为在做出这样的决定时涉及到很多变量,比如变更成本、代码所有权等。我将尝试为您提供最干净的解决方案,然后提供一个需要最少努力的解决方案

在这种情况下,最好应用经典设计模式的作者四人帮的建议。这条建议是:封装变化的内容。在您的情况下,故障处理是变化的,变化基于异常类型。我将如何在这里应用它

第一个解决方案-完全重构代码中的气味 我要问的第一个问题是:您是否可以修改引发异常的代码?如果是这样的话,我会尽量不在捕获异常的代码中进行封装,而是在抛出异常的代码中进行封装。这对您来说可能有些奇怪,但这可能会让您避免冗余。事实上,您的代码当前在两个地方耦合到一种类型的异常

第一个地方是抛出异常的位置,您必须知道要抛出哪个异常-非常简单,它可能如下所示:

 if(someSituationTakesPlace())
 {
   throw new ExpType1();
 }
 else if(someOtherSituationTakesPlace()
 {
   throw new ExpType2();
 }
public class Failure : Exception
{
  IFailureHandling _handling;

  public Failure(IFailureHandling handling)
  {
    //we're injecting how the failure should be handled
    _handling = handling;
  }
  //If you need to provide additional info from 
  //the place where you catch, you can use parameter list of this method
  public void Handle() 
  {
    _handling.Perform();
  }
}
if(sensorsFailed())
{ 
  throw _failureFactory.CreateForCaseWhenSensorsAreDamaged();
}
try
{
  PerformSomeLogic();
} 
catch(Failure failure)
{
  failure.Handle();
}
if(exp.GetType()==typeof(expType1))
{
    if(exp.Message.Include("something went bad"))
    {
      if(exp.InnerException.Message == "Something inside went bad as well";
      {
        DoX();
        DoY();
      }
    }
}
else if (exp.GetType()==typeof(expType2))
{
  DoZ();
  DoV();
}
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessage(message => message.Includes("something went bad"))
   .WithInnerException<SomeInnerException>()
     .HavingAMessage(innerMessage => innerMessage == "Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessageThatIncludes("something went bad")
   .WithInnerException<SomeInnerException>()
     .HavingAMessageEqualTo("Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
当然,条件可能更复杂,可能有多个不同的对象和方法可以抛出,但本质上,它总是归结为一系列的选择,比如在情况a中,抛出异常X

有这种映射的第二个地方是当您捕获异常时——您必须再次通过一系列if-else来找出它是什么情况,然后调用一些逻辑来处理它

为了避免这种冗余,我将决定抛出异常的处理方式——您应该在那里拥有所需的所有信息。因此,我首先定义一个异常类,如下所示:

 if(someSituationTakesPlace())
 {
   throw new ExpType1();
 }
 else if(someOtherSituationTakesPlace()
 {
   throw new ExpType2();
 }
public class Failure : Exception
{
  IFailureHandling _handling;

  public Failure(IFailureHandling handling)
  {
    //we're injecting how the failure should be handled
    _handling = handling;
  }
  //If you need to provide additional info from 
  //the place where you catch, you can use parameter list of this method
  public void Handle() 
  {
    _handling.Perform();
  }
}
if(sensorsFailed())
{ 
  throw _failureFactory.CreateForCaseWhenSensorsAreDamaged();
}
try
{
  PerformSomeLogic();
} 
catch(Failure failure)
{
  failure.Handle();
}
if(exp.GetType()==typeof(expType1))
{
    if(exp.Message.Include("something went bad"))
    {
      if(exp.InnerException.Message == "Something inside went bad as well";
      {
        DoX();
        DoY();
      }
    }
}
else if (exp.GetType()==typeof(expType2))
{
  DoZ();
  DoV();
}
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessage(message => message.Includes("something went bad"))
   .WithInnerException<SomeInnerException>()
     .HavingAMessage(innerMessage => innerMessage == "Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessageThatIncludes("something went bad")
   .WithInnerException<SomeInnerException>()
     .HavingAMessageEqualTo("Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
然后,我将创建一个工厂来创建这样的异常,并在此时使用处理程序绑定它们。例如:

public class FailureFactory
{
  IFailureHandling _handlingOfCaseWhenSensorsAreDown,
  IFailureHandling _handlingOfCaseWhenNetworkConnectionFailed

  public FailureFactory(
    IFailureHandling handlingOfCaseWhenSensorsAreDown,
    IFailureHandling handlingOfCaseWhenNetworkConnectionFailed
    //etc.
    )
  {
    _handlingOfCaseWhenSensorsAreDown 
      = handlingOfCaseWhenSensorsAreDown;
    _handlingOfCaseWhenNetworkConnectionFailed 
      = handlingOfCaseWhenNetworkConnectionFailed;
    //etc.
  }

  public Failure CreateForCaseWhenSensorsAreDamaged()
  {
    return new Failure(_handlingOfCaseWhenSensorsAreDown);
  }

  public Failure CreateForCaseWhenNetworkConnectionFailed()
  {
    return new Failure(_handlingOfCaseWhenNetworkConnectionFailed);
  }
}
通常情况下,每个系统只创建一个这样的工厂,并在实例化所有长期运行对象的位置执行此操作。通常,应用程序中有一个这样的位置,因此,在实例化工厂时,您应该能够通过构造函数传递希望它使用的所有对象,这将创建一个非常原始的fluent界面。记住,流畅的界面是关于可读性和流的,而不仅仅是放.a.dot.every.method.call:-:

var inCaseOfSensorDamagedLogItToDatabaseAndNotifyUser
  = InCaseOfSensorDamagedLogItToDatabaseAndNotifyUser(
      logger, translation, notificationChannel);
var inCaseOfNetworkDownCloseTheApplicationAndDumpMemory 
  = new InCaseOfNetworkDownCloseTheApplicationAndDumpMemory(
      memoryDumpMechanism);

var failureFactory = new FailureFactory(
  inCaseOfSensorDamagedLogItToDatabaseAndNotifyUser,
  inCaseOfNetworkDownCloseTheApplicationAndDumpMemory
);
这样,抛出异常的位置和捕获异常的位置都与处理逻辑分离,这就是问题的不同之处!因此,我们概括了各种变化!当然,在此基础上,您可以免费提供更高级的流畅界面

现在,您抛出异常的每个地方都将如下所示:

 if(someSituationTakesPlace())
 {
   throw new ExpType1();
 }
 else if(someOtherSituationTakesPlace()
 {
   throw new ExpType2();
 }
public class Failure : Exception
{
  IFailureHandling _handling;

  public Failure(IFailureHandling handling)
  {
    //we're injecting how the failure should be handled
    _handling = handling;
  }
  //If you need to provide additional info from 
  //the place where you catch, you can use parameter list of this method
  public void Handle() 
  {
    _handling.Perform();
  }
}
if(sensorsFailed())
{ 
  throw _failureFactory.CreateForCaseWhenSensorsAreDamaged();
}
try
{
  PerformSomeLogic();
} 
catch(Failure failure)
{
  failure.Handle();
}
if(exp.GetType()==typeof(expType1))
{
    if(exp.Message.Include("something went bad"))
    {
      if(exp.InnerException.Message == "Something inside went bad as well";
      {
        DoX();
        DoY();
      }
    }
}
else if (exp.GetType()==typeof(expType2))
{
  DoZ();
  DoV();
}
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessage(message => message.Includes("something went bad"))
   .WithInnerException<SomeInnerException>()
     .HavingAMessage(innerMessage => innerMessage == "Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessageThatIncludes("something went bad")
   .WithInnerException<SomeInnerException>()
     .HavingAMessageEqualTo("Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
在一个地方,您可以捕捉到所有这些异常,如下所示:

 if(someSituationTakesPlace())
 {
   throw new ExpType1();
 }
 else if(someOtherSituationTakesPlace()
 {
   throw new ExpType2();
 }
public class Failure : Exception
{
  IFailureHandling _handling;

  public Failure(IFailureHandling handling)
  {
    //we're injecting how the failure should be handled
    _handling = handling;
  }
  //If you need to provide additional info from 
  //the place where you catch, you can use parameter list of this method
  public void Handle() 
  {
    _handling.Perform();
  }
}
if(sensorsFailed())
{ 
  throw _failureFactory.CreateForCaseWhenSensorsAreDamaged();
}
try
{
  PerformSomeLogic();
} 
catch(Failure failure)
{
  failure.Handle();
}
if(exp.GetType()==typeof(expType1))
{
    if(exp.Message.Include("something went bad"))
    {
      if(exp.InnerException.Message == "Something inside went bad as well";
      {
        DoX();
        DoY();
      }
    }
}
else if (exp.GetType()==typeof(expType2))
{
  DoZ();
  DoV();
}
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessage(message => message.Includes("something went bad"))
   .WithInnerException<SomeInnerException>()
     .HavingAMessage(innerMessage => innerMessage == "Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessageThatIncludes("something went bad")
   .WithInnerException<SomeInnerException>()
     .HavingAMessageEqualTo("Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
这样,您知道如何处理每个故障情况的唯一地方就是在创建FailureFactory类对象的逻辑中

第二个解决方案-使用处理程序 如果您不拥有引发异常的代码,或者在上面概述的解决方案中投入太高的成本或太高的风险,我会使用一个看起来类似FailureFactory的处理程序对象,但不是创建对象,而是自己执行处理:

public class FailureHandlingMechanism
{
  _handlers = Dictionary<Type, IFailureHandling>();

  public FailureHandler(Dictionary<Type, IFailureHandling> handlers)
  {
    _handlers = handlers;
  }

  public void Handle(Exception e)
  {
    //might add some checking whether key is in dictionary
    _handlers[e.GetType()].Perform();
  }
}
实例化这样的处理机制已经为您提供了一个非常原始的流畅界面:

var handlingMechanism = new HandlingMechanism(
  new Dictionary<Type, IFailureHandling>()
  {
    { typeof(NullPointerException), new LogErrorAndCloseApplication()}},
    { typeof(ArgumentException}, new LogErrorAndNotifyUser() }
  };
如果希望以更流畅、噪音更小的方式配置这种处理机制,可以围绕处理机制创建一个生成器,该生成器具有向字典添加键和值的方法以及一个名为Build this的方法 为您创建了对象:

var handlingMechanismThatPerforms = new HandlingMechanismBuilder();
var logErrorAndCloseApplication = new LogErrorAndCloseApplication();
var logErrorAndNotifyUser = new LogErrorAndNotifyUser();

var handlingMechanism = handlingMechanismThatPerforms
  .When<NullPointerException>(logErrorAndCloseApplication)
  .When<ArgumentException>(logErrorAndNotifyUser)
  .Build();

就这样。如果对你有任何帮助,请告诉我

披头士1692,请原谅,我将从重构异常处理开始,而不是直接跳到DSL部分

在你的问题中,你说:

我正在回顾代码库的一部分,接下来是异常处理部分,这部分非常混乱。我想换件更优雅的

我想这是你最关心的。因此,我将尝试为您提供一个如何使其更加优雅的指南-实际上,您提供的代码片段不优雅并不是关于它不是DSL或流畅的接口,而是关于设计质量。如果你的设计中有冗余和耦合,那么在冗余和耦合之上创建一个流畅的界面只会让它变得更糟糕

答案很长,我会参考一些代码质量,所以如果您需要进一步的解释,请告诉我。因为在做出这样的决定时涉及到很多变量,比如变更成本、代码所有权等。我将尝试为您提供最干净的解决方案,然后提供一个需要最少努力的解决方案

在这种情况下,最好应用经典设计模式的作者四人帮的建议。这条建议是:封装变化的内容。在您的情况下,故障处理是变化的,变化基于异常类型。我将如何在这里应用它

第一个解决方案-完全重构代码中的气味 我要问的第一个问题是:您是否可以修改引发异常的代码?如果是这样的话,我会尽量不在捕获异常的代码中进行封装,而是在抛出异常的代码中进行封装。这对您来说可能有些奇怪,但这可能会让您避免冗余。事实上,您的代码当前在两个地方耦合到一种类型的异常

第一个地方是抛出异常的位置,您必须知道要抛出哪个异常-非常简单,它可能如下所示:

 if(someSituationTakesPlace())
 {
   throw new ExpType1();
 }
 else if(someOtherSituationTakesPlace()
 {
   throw new ExpType2();
 }
public class Failure : Exception
{
  IFailureHandling _handling;

  public Failure(IFailureHandling handling)
  {
    //we're injecting how the failure should be handled
    _handling = handling;
  }
  //If you need to provide additional info from 
  //the place where you catch, you can use parameter list of this method
  public void Handle() 
  {
    _handling.Perform();
  }
}
if(sensorsFailed())
{ 
  throw _failureFactory.CreateForCaseWhenSensorsAreDamaged();
}
try
{
  PerformSomeLogic();
} 
catch(Failure failure)
{
  failure.Handle();
}
if(exp.GetType()==typeof(expType1))
{
    if(exp.Message.Include("something went bad"))
    {
      if(exp.InnerException.Message == "Something inside went bad as well";
      {
        DoX();
        DoY();
      }
    }
}
else if (exp.GetType()==typeof(expType2))
{
  DoZ();
  DoV();
}
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessage(message => message.Includes("something went bad"))
   .WithInnerException<SomeInnerException>()
     .HavingAMessage(innerMessage => innerMessage == "Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessageThatIncludes("something went bad")
   .WithInnerException<SomeInnerException>()
     .HavingAMessageEqualTo("Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
当然,条件可能更复杂,可能有多个不同的对象和方法可以抛出,但本质上,它总是归结为一系列的选择,比如在情况a中,抛出异常X

有这种映射的第二个地方是当您捕获异常时——您必须再次通过一系列if-else来找出它是什么情况,然后调用一些逻辑来处理它

为了避免这种冗余,我将决定抛出异常的处理方式——您应该在那里拥有所需的所有信息。因此,我首先定义一个异常类,如下所示:

 if(someSituationTakesPlace())
 {
   throw new ExpType1();
 }
 else if(someOtherSituationTakesPlace()
 {
   throw new ExpType2();
 }
public class Failure : Exception
{
  IFailureHandling _handling;

  public Failure(IFailureHandling handling)
  {
    //we're injecting how the failure should be handled
    _handling = handling;
  }
  //If you need to provide additional info from 
  //the place where you catch, you can use parameter list of this method
  public void Handle() 
  {
    _handling.Perform();
  }
}
if(sensorsFailed())
{ 
  throw _failureFactory.CreateForCaseWhenSensorsAreDamaged();
}
try
{
  PerformSomeLogic();
} 
catch(Failure failure)
{
  failure.Handle();
}
if(exp.GetType()==typeof(expType1))
{
    if(exp.Message.Include("something went bad"))
    {
      if(exp.InnerException.Message == "Something inside went bad as well";
      {
        DoX();
        DoY();
      }
    }
}
else if (exp.GetType()==typeof(expType2))
{
  DoZ();
  DoV();
}
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessage(message => message.Includes("something went bad"))
   .WithInnerException<SomeInnerException>()
     .HavingAMessage(innerMessage => innerMessage == "Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessageThatIncludes("something went bad")
   .WithInnerException<SomeInnerException>()
     .HavingAMessageEqualTo("Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
然后,我将创建一个工厂来创建这样的异常,并在此时使用处理程序绑定它们。例如:

public class FailureFactory
{
  IFailureHandling _handlingOfCaseWhenSensorsAreDown,
  IFailureHandling _handlingOfCaseWhenNetworkConnectionFailed

  public FailureFactory(
    IFailureHandling handlingOfCaseWhenSensorsAreDown,
    IFailureHandling handlingOfCaseWhenNetworkConnectionFailed
    //etc.
    )
  {
    _handlingOfCaseWhenSensorsAreDown 
      = handlingOfCaseWhenSensorsAreDown;
    _handlingOfCaseWhenNetworkConnectionFailed 
      = handlingOfCaseWhenNetworkConnectionFailed;
    //etc.
  }

  public Failure CreateForCaseWhenSensorsAreDamaged()
  {
    return new Failure(_handlingOfCaseWhenSensorsAreDown);
  }

  public Failure CreateForCaseWhenNetworkConnectionFailed()
  {
    return new Failure(_handlingOfCaseWhenNetworkConnectionFailed);
  }
}
通常情况下,每个系统只创建一个这样的工厂,并在实例化所有长期运行对象的位置执行此操作。通常,应用程序中有一个这样的位置,因此,在实例化工厂时,您应该能够通过构造函数传递希望它使用的所有对象,这将创建一个非常原始的fluent界面。记住,流畅的界面是关于可读性和流的,而不仅仅是放.a.dot.every.method.call:-:

var inCaseOfSensorDamagedLogItToDatabaseAndNotifyUser
  = InCaseOfSensorDamagedLogItToDatabaseAndNotifyUser(
      logger, translation, notificationChannel);
var inCaseOfNetworkDownCloseTheApplicationAndDumpMemory 
  = new InCaseOfNetworkDownCloseTheApplicationAndDumpMemory(
      memoryDumpMechanism);

var failureFactory = new FailureFactory(
  inCaseOfSensorDamagedLogItToDatabaseAndNotifyUser,
  inCaseOfNetworkDownCloseTheApplicationAndDumpMemory
);
这样,抛出异常的位置和捕获异常的位置都与处理逻辑分离,这就是问题的不同之处!因此,我们概括了各种变化!当然,在此基础上,您可以免费提供更高级的流畅界面

现在,您抛出异常的每个地方都将如下所示:

 if(someSituationTakesPlace())
 {
   throw new ExpType1();
 }
 else if(someOtherSituationTakesPlace()
 {
   throw new ExpType2();
 }
public class Failure : Exception
{
  IFailureHandling _handling;

  public Failure(IFailureHandling handling)
  {
    //we're injecting how the failure should be handled
    _handling = handling;
  }
  //If you need to provide additional info from 
  //the place where you catch, you can use parameter list of this method
  public void Handle() 
  {
    _handling.Perform();
  }
}
if(sensorsFailed())
{ 
  throw _failureFactory.CreateForCaseWhenSensorsAreDamaged();
}
try
{
  PerformSomeLogic();
} 
catch(Failure failure)
{
  failure.Handle();
}
if(exp.GetType()==typeof(expType1))
{
    if(exp.Message.Include("something went bad"))
    {
      if(exp.InnerException.Message == "Something inside went bad as well";
      {
        DoX();
        DoY();
      }
    }
}
else if (exp.GetType()==typeof(expType2))
{
  DoZ();
  DoV();
}
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessage(message => message.Includes("something went bad"))
   .WithInnerException<SomeInnerException>()
     .HavingAMessage(innerMessage => innerMessage == "Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessageThatIncludes("something went bad")
   .WithInnerException<SomeInnerException>()
     .HavingAMessageEqualTo("Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
在一个地方,您可以捕捉到所有这些异常,如下所示:

 if(someSituationTakesPlace())
 {
   throw new ExpType1();
 }
 else if(someOtherSituationTakesPlace()
 {
   throw new ExpType2();
 }
public class Failure : Exception
{
  IFailureHandling _handling;

  public Failure(IFailureHandling handling)
  {
    //we're injecting how the failure should be handled
    _handling = handling;
  }
  //If you need to provide additional info from 
  //the place where you catch, you can use parameter list of this method
  public void Handle() 
  {
    _handling.Perform();
  }
}
if(sensorsFailed())
{ 
  throw _failureFactory.CreateForCaseWhenSensorsAreDamaged();
}
try
{
  PerformSomeLogic();
} 
catch(Failure failure)
{
  failure.Handle();
}
if(exp.GetType()==typeof(expType1))
{
    if(exp.Message.Include("something went bad"))
    {
      if(exp.InnerException.Message == "Something inside went bad as well";
      {
        DoX();
        DoY();
      }
    }
}
else if (exp.GetType()==typeof(expType2))
{
  DoZ();
  DoV();
}
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessage(message => message.Includes("something went bad"))
   .WithInnerException<SomeInnerException>()
     .HavingAMessage(innerMessage => innerMessage == "Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessageThatIncludes("something went bad")
   .WithInnerException<SomeInnerException>()
     .HavingAMessageEqualTo("Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
这样,您知道如何处理每个故障情况的唯一地方就是在创建FailureFactory类对象的逻辑中

第二个解决方案-使用处理程序 如果您不拥有引发异常的代码,或者在上面概述的解决方案中投入太高的成本或太高的风险,我会使用一个看起来类似FailureFactory的处理程序对象,但不是创建对象,而是自己执行处理:

public class FailureHandlingMechanism
{
  _handlers = Dictionary<Type, IFailureHandling>();

  public FailureHandler(Dictionary<Type, IFailureHandling> handlers)
  {
    _handlers = handlers;
  }

  public void Handle(Exception e)
  {
    //might add some checking whether key is in dictionary
    _handlers[e.GetType()].Perform();
  }
}
实例化这样的处理机制已经为您提供了一个非常原始的流畅界面:

var handlingMechanism = new HandlingMechanism(
  new Dictionary<Type, IFailureHandling>()
  {
    { typeof(NullPointerException), new LogErrorAndCloseApplication()}},
    { typeof(ArgumentException}, new LogErrorAndNotifyUser() }
  };
如果希望以更流畅、噪音更小的方式配置此类处理机制,可以围绕处理机制创建一个生成器,该生成器具有向字典添加键和值的方法,以及一个名为Build的方法,该方法为您创建了对象:

var handlingMechanismThatPerforms = new HandlingMechanismBuilder();
var logErrorAndCloseApplication = new LogErrorAndCloseApplication();
var logErrorAndNotifyUser = new LogErrorAndNotifyUser();

var handlingMechanism = handlingMechanismThatPerforms
  .When<NullPointerException>(logErrorAndCloseApplication)
  .When<ArgumentException>(logErrorAndNotifyUser)
  .Build();

就这样。如果对你有任何帮助,请告诉我

这是我第二次尝试回答你的问题。正如我正确理解的那样,您试图从这样的代码中摆脱嵌套的ifs和ELSE:

 if(someSituationTakesPlace())
 {
   throw new ExpType1();
 }
 else if(someOtherSituationTakesPlace()
 {
   throw new ExpType2();
 }
public class Failure : Exception
{
  IFailureHandling _handling;

  public Failure(IFailureHandling handling)
  {
    //we're injecting how the failure should be handled
    _handling = handling;
  }
  //If you need to provide additional info from 
  //the place where you catch, you can use parameter list of this method
  public void Handle() 
  {
    _handling.Perform();
  }
}
if(sensorsFailed())
{ 
  throw _failureFactory.CreateForCaseWhenSensorsAreDamaged();
}
try
{
  PerformSomeLogic();
} 
catch(Failure failure)
{
  failure.Handle();
}
if(exp.GetType()==typeof(expType1))
{
    if(exp.Message.Include("something went bad"))
    {
      if(exp.InnerException.Message == "Something inside went bad as well";
      {
        DoX();
        DoY();
      }
    }
}
else if (exp.GetType()==typeof(expType2))
{
  DoZ();
  DoV();
}
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessage(message => message.Includes("something went bad"))
   .WithInnerException<SomeInnerException>()
     .HavingAMessage(innerMessage => innerMessage == "Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessageThatIncludes("something went bad")
   .WithInnerException<SomeInnerException>()
     .HavingAMessageEqualTo("Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
现在考虑一下,您已经创建了一个链式API,其内容如下:

 if(someSituationTakesPlace())
 {
   throw new ExpType1();
 }
 else if(someOtherSituationTakesPlace()
 {
   throw new ExpType2();
 }
public class Failure : Exception
{
  IFailureHandling _handling;

  public Failure(IFailureHandling handling)
  {
    //we're injecting how the failure should be handled
    _handling = handling;
  }
  //If you need to provide additional info from 
  //the place where you catch, you can use parameter list of this method
  public void Handle() 
  {
    _handling.Perform();
  }
}
if(sensorsFailed())
{ 
  throw _failureFactory.CreateForCaseWhenSensorsAreDamaged();
}
try
{
  PerformSomeLogic();
} 
catch(Failure failure)
{
  failure.Handle();
}
if(exp.GetType()==typeof(expType1))
{
    if(exp.Message.Include("something went bad"))
    {
      if(exp.InnerException.Message == "Something inside went bad as well";
      {
        DoX();
        DoY();
      }
    }
}
else if (exp.GetType()==typeof(expType2))
{
  DoZ();
  DoV();
}
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessage(message => message.Includes("something went bad"))
   .WithInnerException<SomeInnerException>()
     .HavingAMessage(innerMessage => innerMessage == "Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessageThatIncludes("something went bad")
   .WithInnerException<SomeInnerException>()
     .HavingAMessageEqualTo("Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
让它:

if(situationA)
{
  throw ExceptionXYZ();
}
else if (situationB)
{
  throw ExceptionCVZF();
}
那么就不需要嵌套的if了 -您的异常处理将是:

try
{
  XYZ()
}
catch(ExceptionXYZ)
{
  DoX();
  DoY();
}
catch(ExceptionCVZF)
{
  DoZ();
  DoV();
}

这是我第二次尝试回答你的问题。正如我正确理解的那样,您试图从这样的代码中摆脱嵌套的ifs和ELSE:

 if(someSituationTakesPlace())
 {
   throw new ExpType1();
 }
 else if(someOtherSituationTakesPlace()
 {
   throw new ExpType2();
 }
public class Failure : Exception
{
  IFailureHandling _handling;

  public Failure(IFailureHandling handling)
  {
    //we're injecting how the failure should be handled
    _handling = handling;
  }
  //If you need to provide additional info from 
  //the place where you catch, you can use parameter list of this method
  public void Handle() 
  {
    _handling.Perform();
  }
}
if(sensorsFailed())
{ 
  throw _failureFactory.CreateForCaseWhenSensorsAreDamaged();
}
try
{
  PerformSomeLogic();
} 
catch(Failure failure)
{
  failure.Handle();
}
if(exp.GetType()==typeof(expType1))
{
    if(exp.Message.Include("something went bad"))
    {
      if(exp.InnerException.Message == "Something inside went bad as well";
      {
        DoX();
        DoY();
      }
    }
}
else if (exp.GetType()==typeof(expType2))
{
  DoZ();
  DoV();
}
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessage(message => message.Includes("something went bad"))
   .WithInnerException<SomeInnerException>()
     .HavingAMessage(innerMessage => innerMessage == "Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessageThatIncludes("something went bad")
   .WithInnerException<SomeInnerException>()
     .HavingAMessageEqualTo("Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
现在考虑一下,您已经创建了一个链式API,其内容如下:

 if(someSituationTakesPlace())
 {
   throw new ExpType1();
 }
 else if(someOtherSituationTakesPlace()
 {
   throw new ExpType2();
 }
public class Failure : Exception
{
  IFailureHandling _handling;

  public Failure(IFailureHandling handling)
  {
    //we're injecting how the failure should be handled
    _handling = handling;
  }
  //If you need to provide additional info from 
  //the place where you catch, you can use parameter list of this method
  public void Handle() 
  {
    _handling.Perform();
  }
}
if(sensorsFailed())
{ 
  throw _failureFactory.CreateForCaseWhenSensorsAreDamaged();
}
try
{
  PerformSomeLogic();
} 
catch(Failure failure)
{
  failure.Handle();
}
if(exp.GetType()==typeof(expType1))
{
    if(exp.Message.Include("something went bad"))
    {
      if(exp.InnerException.Message == "Something inside went bad as well";
      {
        DoX();
        DoY();
      }
    }
}
else if (exp.GetType()==typeof(expType2))
{
  DoZ();
  DoV();
}
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessage(message => message.Includes("something went bad"))
   .WithInnerException<SomeInnerException>()
     .HavingAMessage(innerMessage => innerMessage == "Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessageThatIncludes("something went bad")
   .WithInnerException<SomeInnerException>()
     .HavingAMessageEqualTo("Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });
让它:

if(situationA)
{
  throw ExceptionXYZ();
}
else if (situationB)
{
  throw ExceptionCVZF();
}
然后您将不需要嵌套的ifs-您的异常处理将是:

try
{
  XYZ()
}
catch(ExceptionXYZ)
{
  DoX();
  DoY();
}
catch(ExceptionCVZF)
{
  DoZ();
  DoV();
}

我编写了一个简单的fluent异常处理程序。它易于扩展。您可以在这里查看它:也许它可以根据您的目标进行定制。

我编写了一个简单的fluent异常处理程序。它易于扩展。您可以在这里查看:也许可以根据您的目标进行定制。

fluent界面能为您带来什么?它将帮助我防止大量嵌套if,并且代码将易于维护。嗯,我想马丁并不是什么都对。@JohnSaunders:对于我们这些没有运行时宏功能的人来说,这是一种穷人的DSL形式,例如C程序员。fluent界面会给你买什么?它将帮助我防止大量嵌套if,代码也将很容易维护。嗯,我想马丁并不是什么都对。@JohnSaunders:对于我们这些没有运行时宏功能的人来说,这是一种可怜的DSL形式,比如C程序员。