请选择 进入手机版 | 继续访问电脑版

湖南新梦想

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 623|回复: 0

.Net 异常最佳做法(上)

[复制链接]

2775

主题

3174

帖子

1万

积分

论坛元老

Rank: 8Rank: 8

积分
11260
发表于 2020-11-18 10:17:03 | 显示全部楼层 |阅读模式
常信息原因
  异常是易于滥用的那些构造之一。这可能包括不应该在应有的情况下引发异常或在没有充分理由的情况下捕获异常。还有一个引发错误异常的问题,它不仅无助于我们,而且会使我们困惑。另一方面,存在正确处理异常的问题。如果使用不当,异常处理会变得更糟。所以,在本文中,我将简单介绍一些有关引发和处理异常的最佳实践。展示如何抛出适当的异常可以为我们节省很多调试方面的麻烦。我还将讨论当我们想要查找错误时不良的异常处理如何引起误导。
  抛出异常
  何时抛出异常
  在很多情况下,抛出异常是有意义的,在这里,我将对其进行描述并讨论为什么抛出它们是一个好主意。请注意,本文中的许多示例都经过简化以证明这一点。例如,没有使用一种方法来检索数组元素。或者在某些情况下,我没有使用以前提倡的[url=]技术[/url]来关注当前观点。因此,自然而然地,示例并不试图在所有方面都成为异常处理的理想形式,因为这样会引入额外的元素,从而可能使读者分心。
  1:不可能完成过程并给出结果(快速失败)
static void FindWinner(int[] winners)
{
   if (winners == null)
{
throw new System.ArgumentNullException($"参数 {nameof(winners)} 不能为空", nameof(winners)); }
   OtherMethodThatUsesTheArray(winner);
}

  假设我们有上述方法,在这里我们抛出一个异常,因为从这种方法中获得没有赢家数组的结果是不可能的。另一个要点是该方法的用户易于使用。想象一下,我们没有引发异常,而是将数组传递给OtherMethodThatUsesTheArraymethod,而该方法引发了NullReferenceException。通过不抛出异常,调试变得更加困难。因为此代码的调试器必须首先查看OtherMethodThatUsesTheArray方法,因为这就是错误的来源。然后,他找出赢家的论点是产生此错误的地方。当我们抛出异常时,我们确切地知道错误的根源,而不必在代码库中追逐错误。另一个问题是不必要的资源使用,假设在达到框架发生异常之前,我们进行了一些昂贵的处理。现在,如果该方法在没有相关参数或您所没有的情况下无法提供我们的结果,则实际上浪费了很多资源,而实际上该方法最初可能无法成功。请记住,当可能发生错误时,我们不会抛出异常,但是当错误阻止流程时,我们会抛出异常。有时我们也可以避免使用异常,而使用try-stuff模式int.TryParse并且不抛出异常。
  2:给定对象的当前状态,调用对象的成员可能会产生无效的结果,或者可能会完全失败
void WriteLog(FileStream logFile)
{
if (!logFile.CanWrite)
{
throw new System.InvalidOperationException("日志文件不能是只读的");
} // Else write data to the log and return.
}

  在这里,传递给WriteLog方法的文件流以不可写的方式进行创建。在这种情况下,我们知道此方法将不起作用。因为我们无法登录到不可写的文件。另一个例子是当我们有一个类,我们希望在收到它时处于特定状态。通过抛出异常并节省资源和调试时间,我们又一次快速失败。
  3:捕获通用的非特定异常并引发更特定的异常
void WriteLog(FileStream logFile)
{
if (!logFile.CanWrite)
{
throw new System.InvalidOperationException("日志文件不能是只读的");
}
// Else write data to the log and return.
}

  关于异常,有一个经验法则,程序产生的异常越具体,调试和维护就越容易。换句话说,通过这样做,我们的程序会产生更准确的错误。因此,我们应始终努力尽可能地抛出更具体的异常。这就是为什么抛出异常喜欢System.Exception,System.SystemException,System.NullReferenceException,或者System.IndexOutOfRangeException是不是一个好主意。最好不要使用它们,并且将它们看作是错误消息是由框架生成的信号,我们将花费大量时间进行调试。在上面的代码中,您看到我们抓住IndexOutOfRangeException并抛出了一个新的ArgumentOutOfRangeException向我们显示了实际错误的来源。我们还可以使用它来捕获框架生成的异常并引发新的自定义异常。这使我们可以添加其他信息,或者可能以不同的方式进行处理。只要确保将原始异常作为内部异常传递到自定义异常中,否则stacktrace将丢失。
  4:例外情况引发异常
  这听起来似乎很明显,但有时可能会很棘手。我们的程序中有某些事情会发生,我们不能将它们视为错误。因此,我们不会抛出异常。例如,搜索查询可能返回空,或者用户登录尝试可能失败。在这种情况下,最好返回某种有意义的消息,然后抛出异常。正如史蒂夫·麦康奈尔(SteveMcConnell)在《代码完整》一书中所说的那样,“例外应该保留给真正的例外”,而不是期望的例外。
  不要使用异常来更改程序的流程
  以下面的代码为例。
[HttpPost]
public ViewResult CreateProduct(CreateProductViewModel viewModel)
{
try
{
ValidateProductViewModel(viewModel);
CreateProduct(viewModel);
}
catch (ValidationException ex)
{ return View(viewModel);
}
}

  我在一些需要处理的旧代码中看到了这种模式。如您所见,ValidateProductViewModel如果视图模型无效,则由于某种原因该方法将引发异常。然后,如果视图模型无效,它将捕获该模型并返回错误的视图。我们最好将上面的代码更改为下面的代码
[HttpPost]
public ViewResult CreateProduct(CreateProduct viewModel)
{
bool viewModelIsValid = ValidateProductViewModel(viewModel);
if(!viewModelIsValid) return View(viewModel);
CreateProduct(viewModel);
return View(viewModel);
}

  在这里,负责验证的方法在视图模型无效的情况下返回布尔值,而不是引发异常
  不返回错误代码,而是引发异常
  抛出异常总是比返回错误代码更安全。原因是如果调用代码忘记检查或返回错误代码并继续执行该怎么办?但是,如果我们抛出异常,那将不会发生。
  确保清除抛出异常的任何副作用
private static void MakeDeposit(Account account,decimal amount)
{
try
{
account.Deposit(amount);
}
catch
{
account.RollbackDeposit(amount);
throw;
}
}

  在这里,我们知道调用deposit方法时可能会发生错误。我们应该确保如果发生异常,则对系统的任何更改都会回滚。
try
{
DBConnection.Save();
}
catch
{
// 回滚[url=]数据库[/url]操作
DBConnection.Rollback();
// 重新抛出异常,让外界知道错误消息
throw;
}



    更多精彩下节再见哦!



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|湖南新梦想 ( 湘ICP备18019834号-2 )

GMT+8, 2022-6-29 14:15 , Processed in 0.043390 second(s), 20 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表