【问题标题】:Handling a class that doesn't throw exception in c#在c#中处理一个不抛出异常的类
【发布时间】:2010-08-04 11:51:46
【问题描述】:

我有一些如下所示的 UI 代码:

try
{
    SomeClass classInstance = new SomeClass(someId);
}
catch (Exception exception)
{
    // Content wasn't created, show a message, stop processing
    return;
}

似乎添加了 try catch,因为如果 SomeClass 的构造函数接收到的 someId 无效,并且无法在 DB 中找到数据,那么它的构造函数就会崩溃。

最近通过 FXCop 运行此代码,它警告不要使用一般异常,但 SomeClass 所做的只是抛出一个新异常,并显示一条消息说它无法初始化。

我想问题是类构造函数应该有它自己的自定义异常,然后我可以在我的 UI 中处理它,但我想知道我还能对上面的代码做些什么来处理满足 FXCop 要求的异常?

【问题讨论】:

  • 如果SomeClass 抛出一个通用的Exception,那么您的代码是(据我所知)捕获它的唯一方法。这里的问题在于SomeClass 的作者 - 告诉 FXCop 在这个问题上别管它。
  • 我同意 - 在 SomeClass 不接受修改的情况下,这可能是我必须考虑的。值得庆幸的是,我从作者那里继承了 SomeClass,所以我可以继续以正确的方式修复他的代码——上面的问题更像是“如果我做不到怎么办?”场景。

标签: c# exception exception-handling try-catch


【解决方案1】:

FxCop 的规则之所以存在,是因为上面的 catch (Exception) 块捕获了所有可能的异常,包括像 StackOverflowException 这样的低级异常,您可能无法以有用的方式捕获这些异常。

正确的方法肯定是抛出一个更具体的类型:要么是你发明的,要么是与你的情况非常匹配的现有 .NET 框架异常类型。 (如果有疑问,我通常会选择InvalidOperationException。)

或者,您可以在捕获时检查确切的异常类型。这不会阻止 FxCop 警告,但它应该解决根本问题:

catch (Exception exception)
{
    if (exception.GetType() == typeof(Exception))
    {
        // Content wasn't created, show a message, stop processing
        return;
    }
    else
    {
        // Some other exception type that wasn't thrown from our code -
        //    delegate to a higher-level exception handler
        throw;
    }
}

【讨论】:

  • 谢谢,这证实了我的想法,如果 SomeClass 的作者没有正确完成他的工作,并且您没有访问权限,那么您几乎必须从FXCop 报告,因为你无法取悦它。值得庆幸的是,我继承了作者的工作,所以我可以完全访问源代码并且可以做我想做的事 - 在我的情况下,我会将其修改为 ArgumentException。
【解决方案2】:

你不需要自定义异常;在给定的情况下,只需使用框架中已经存在的几十个之一。如果someId 不好,请抛出ArgumentException——这就是它的用途。如果某些不应该为 null 的内容,则会出现 NullReferenceException;让它被扔掉。等等。抛出一个普通的Exception 有点像说“出了点问题 - 阅读消息以了解详细信息”而不是“这出了问题”。

FxCop 抱怨catch (Exception),因为它被滥用太普遍了,无法吞下所有异常,而不是让它们传播并由知道如何这样做的代码处理。您应该能够说出正在抛出的异常类型并捕获这些异常,同时让那些您不认识的异常进入调用堆栈。

【讨论】:

  • 你应该永远抛出NullReferenceException。大多数时候你可以使用ArgumentNullException。不过,您帖子的其余部分非常有意义。
  • 你永远不应该必须抛出NullReferenceException——框架会为你做到这一点。让它传播更容易。但是ArgumentNullException 只对 arguments 有意义(因此得名),如果你被传递的不是 null,如果你扔它会混淆人们。
  • 这就是我大部分时间都这么说的原因。我发现 90% 的情况下,我希望在某事物为空时发生异常——即当某事物是一个参数时。对于其余的情况,你是对的,就让它发生吧。
  • 问题是你必须非常小心:框架会捕获很多标准异常,然后你永远不知道它去了哪里。 MSDN 上某处有一个已发布列表(没时间找到它,抱歉),其中包含大约六种 CLR 明确not 捕获的异常类型 - 我很确定NullReferenceException 不在那个清单。所以我完全同意伊戈尔的原始评论。
  • 框架吃异常?考虑到它被认为是多么邪恶,这似乎是一件很奇怪的事情。
【解决方案3】:

您应该修复类构造函数。抛出 Exception永远一个好主意。

但是,要临时解决您遇到的问题(因为这是一种可怕的、不可靠的黑客攻击),您可以将异常消息与您期望的消息进行对比:

catch (Exception exception)
{
    if (exception.Message == "whatever your class sets the message to")
        // Content wasn't created, show a message, stop processing
        return;
    else
        // Any other exception should bubble
        throw;
}

【讨论】:

  • 这实际上会阻止 FXCop 抱怨捕获异常吗?
  • 不,它不会阻止 FxCop 的抱怨,但它解决了 FxCop 抱怨的原因 - 捕获Exception 的范围太广了。
【解决方案4】:

使用InvalidOperationException 代替投掷Exception 听起来可能是明智的。

【讨论】:

  • 查看异常描述,这听起来确实很合适,但我认为我更愿意在构造实例之外的操作中使用它。我认为就我而言, ArgumentException 更合适,正如其他人所建议的那样,因为参数 id 无效。
【解决方案5】:

如果 FXCop 不喜欢处理一般异常(我倾向于同意),那么也许您可以访问 SomeClass 的源代码。修改构造函数以抛出更具体的异常,例如ArgumentOutOfRangeException 或一些自定义异常。

在这种情况下,您的代码将如下所示:

try
{
   SomeClass classInstance = new SomeClass(someId);
}
catch(ArgumentOutOfRangeException exception)
{
   // Content wasn't created, show a message, stop processing
   return;
}

【讨论】:

  • 我的问题是“如果我无法访问 SomeClass 的源代码怎么办?”风格。在这种情况下,我这样做了,并且已经用一个更相关的异常修复了它,但是在这样做的时候,我想知道如果我遇到一个我无法修改的类会发生什么。
  • 把它扔掉,再找一个写得还不错的。例外应该有意义; “出了什么问题”是没有意义的,如果你真正参与到使用这个类中并且突然从你的应用程序的某个地方得到一个“发生了某事”的错误,那会让你陷入地狱。刚开始时并非如此,因此请在编写太多使用该类的代码之前切换。
【解决方案6】:

正如许多其他人所说,构造函数不应该抛出一个赤裸裸的Exception。看到构造函数从DB取数据并根据结果抛出,最好的解决办法就是自己创建异常类。

在 Visual Studio 中创建异常非常简单。只需输入Exception 并按TAB。然后它将使用所需的构造函数(全部四个)创建异常类。不要害怕创建不做太多事情的类,这就是它们的设计目的。

这就是我编写这个类的方式:

public class SomeClass {
    public SomeClass(int someId) {
        if (someId < 0) //validation on the ID, can it be negative?
            throw new ArgumentException("someId", "ID cannot be negative");

        //Perform DB operation
        if (/*DB error - not found*/)
            throw new DataNotFoundException("Cannot find record with ID " + someId);
    }
}

[Serializable]
public class DataNotFoundException : Exception {
    public DataNotFoundException() { }
    public DataNotFoundException(string message) : base(message) { }
    public DataNotFoundException(string message, Exception inner) : base(message, inner) { }
    protected DataNotFoundException(
      System.Runtime.Serialization.SerializationInfo info,
      System.Runtime.Serialization.StreamingContext context)
        : base(info, context) { }
}

【讨论】:

  • 谢谢,这是一个有用的快捷方式——我以前没有创建过自定义异常,但到时候我会记住的。目前,我使用的是标准的 ArgumentException,它适合我的需求。
猜你喜欢
  • 2022-01-18
  • 2011-02-21
  • 2011-12-12
  • 2013-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-19
  • 2020-02-13
相关资源
最近更新 更多