65

这真的,真的让我很生气,所以我希望有人能给我一个合理的理由来解释为什么事情是这样的。

未实现异常。 你在拉我的腿,对吧?

不,我不会轻描淡写地说:“等一下,该方法已实现 - 它会抛出 NotImplementedException。” 是的,没错,您必须实现抛出 NotImplementedException 的方法(与 C++ 中的纯虚函数调用不同——现在这很有意义!)。虽然这非常有趣,但我的脑海里还有一个更严重的问题。

我只是想知道,在存在 NotImplementedException 的情况下,任何人都可以用 .Net 做任何事情吗?您是否希望使用 try catch 块包装每个抽象方法调用以防止可能未实现的方法?如果你捕捉到这样的异常,你到底应该用它做什么?

我看不到在不调用方法的情况下测试方法是否实际实现的方法。由于调用它可能会产生副作用,因此我无法预先进行所有检查然后运行我的算法。我必须运行我的算法,捕捉 NotImplementedExceptions 和一些如何将我的应用程序回滚到某个理智状态。

这很疯狂。疯狂的。疯狂的。所以问题是: 为什么 NotImplementedException 存在

作为先发制人,我不希望任何人回应,“因为设计师需要将其放入自动生成的代码中。” 这太可怕了。我宁愿在您提供实现之前不编译自动生成的代码。例如,自动生成的实现可以是“throw NotImplementedException;” NotImplementedException 未定义的地方!

有没有人捕获并处理过 NotImplementedException?你有没有在你的代码中留下 NotImplementedException ?如果是这样,这是否代表了一个定时炸弹(即,您不小心把它留在了那里),还是一个设计缺陷(该方法不应该被实现并且永远不会被调用)?

我也很怀疑 NotSupportedException ......不支持?什么?如果不支持,为什么它是您界面的一部分?Microsoft 的任何人都可以拼写不正确的继承吗?但是,如果我没有为此受到太多滥用,我可能会为此提出另一个问题。

附加信息:

是有关该主题的有趣读物。

似乎与Brad Abrams达成了强烈的共识,即“NotImplementedException 适用于尚未实现但确实应该(并且将会)实现的功能。就像您在构建类时可能会开始的那样,获取所有方法那里抛出 NotImplementedException,然后用真正的代码将它们清除掉……”

Jared Parsons的评论很弱,可能应该被忽略: NotImplementedException:当一个类型由于任何其他原因没有实现方法时抛出这个异常。

MSDN在这个主题上甚至更弱,只是声明“当请求的方法或操作未实现时引发的异常”。

4

27 回答 27

142

在一种情况下我觉得它很有用:TDD。

我编写测试,然后创建存根以便编译测试。这些存根除了throw new NotImplementedException();. 这样测试将默认失败,无论如何。如果我使用了一些虚拟返回值,它可能会产生误报。现在所有测试都编译并失败了,因为没有实现,我解决了那些存根。

因为我从不NotImplementedException在任何其他情况下使用 a,所以 noNotImplementedException永远不会传递到发布代码,因为它总是会使某些测试失败。

你不需要到处抓它。好的 API 会记录抛出的异常。这些是你应该寻找的。

编辑:我写了一个 FxCop 规则来找到它们。

这是代码:

using System;
using Microsoft.FxCop.Sdk;

/// <summary>
/// An FxCop rule to ensure no <see cref="NotImplementedException"/> is
/// left behind on production code.
/// </summary>
internal class DoNotRaiseNotImplementedException : BaseIntrospectionRule
{
    private TypeNode _notImplementedException;
    private Member _currentMember;

    public DoNotRaiseNotImplementedException()
        : base("DoNotRaiseNotImplementedException",
               // The following string must be the assembly name (here
               // Bevonn.CodeAnalysis) followed by a dot and then the
               // metadata file name without the xml extension (here
               // DesignRules). See the note at the end for more details.
               "Bevonn.CodeAnalysis.DesignRules",
               typeof (DoNotRaiseNotImplementedException).Assembly) { }

    public override void BeforeAnalysis()
    {
        base.BeforeAnalysis();
        _notImplementedException = FrameworkAssemblies.Mscorlib.GetType(
            Identifier.For("System"),
            Identifier.For("NotImplementedException"));
    }

    public override ProblemCollection Check(Member member)
    {
        var method = member as Method;
        if (method != null)
        {
            _currentMember = member;
            VisitStatements(method.Body.Statements);
        }
        return Problems;
    }

    public override void VisitThrow(ThrowNode throwInstruction)
    {
        if (throwInstruction.Expression != null &&
            throwInstruction.Expression.Type.IsAssignableTo(_notImplementedException))
        {
            var problem = new Problem(
                GetResolution(),
                throwInstruction.SourceContext,
                _currentMember.Name.Name);
            Problems.Add(problem);
        }
    }
}

这是规则元数据:

<?xml version="1.0" encoding="utf-8" ?>
<Rules FriendlyName="Bevonn Design Rules">
  <Rule TypeName="DoNotRaiseNotImplementedException" Category="Bevonn.Design" CheckId="BCA0001">
    <Name>Do not raise NotImplementedException</Name>
    <Description>NotImplementedException should not be used in production code.</Description>
    <Url>http://stackoverflow.com/questions/410719/notimplementedexception-are-they-kidding-me</Url>
    <Resolution>Implement the method or property accessor.</Resolution>
    <MessageLevel Certainty="100">CriticalError</MessageLevel>
    <Email></Email>
    <FixCategories>NonBreaking</FixCategories>
    <Owner></Owner>
  </Rule>
</Rules>

要构建它,您需要:

  • 参考Microsoft.FxCop.Sdk.dllMicrosoft.Cci.dll

  • 将元数据放在一个名为的文件中DesignRules.xml,并将其作为嵌入式资源添加到您的程序集中

  • 命名您的程序集Bevonn.CodeAnalysis。如果要对元数据或程序集文件使用不同的名称,请确保将第二个参数相应地更改为基本构造函数。

然后只需将生成的程序集添加到您的 FxCop 规则中,并从您宝贵的代码中删除那些该死的异常。在某些极端情况下,当抛出一个 NotImplementedException 时它不会报告,但我真的认为如果你真的在编写这样的 cthulhian 代码,你是没有希望的。对于正常使用,即throw new NotImplementedException();,它可以工作,这就是最重要的。

于 2009-01-04T10:34:43.530 回答
40

它用于支持一个相当常见的用例,一个有效但仅部分完成的 API。假设我想让开发人员测试和评估我的 API -WashDishes()至少在我的机器上有效,但我还没有开始编码DryDishes(),更不用说PutAwayDishes()。与其默默地失败,或者给出一些神秘的错误信息,我可以很清楚为什么DryDishes()不起作用——我还没有实现它。

它的姊妹例外NotSupportedException主要对提供者模型有意义。很多洗碗机都有烘干功能,所以属于界面,但我的折扣洗碗机不支持。我可以通过NotSupportedException

于 2009-01-04T09:32:31.377 回答
28

我将在一个地方总结我对此的看法,因为它们分散在一些评论中:

  1. 您用于NotImplementedException指示接口成员尚未实现,但将会实现。您将其与自动化单元测试或 QA 测试相结合,以确定仍需要实现的功能。

  2. 实现该功能后,您将删除NotImplementedException. 为该功能编写了新的单元测试,以确保其正常工作。

  3. NotSupportedException通常用于不支持对特定类型没有意义的功能的提供者。在这些情况下,特定类型会抛出异常,客户端会捕获它们并酌情处理它们。

  4. NotImplementedException和两者NotSupportedException存在于框架中的原因很简单:导致它们的情况很常见,因此在框架中定义它们是有意义的,这样开发人员就不必不断地重新定义它们。此外,它使客户端很容易知道要捕获哪个异常(尤其是在单元测试的上下文中)。如果您必须定义自己的异常,他们必须弄清楚要捕获哪个异常,这至少会适得其反,而且经常是不正确的。

于 2009-01-04T12:02:57.563 回答
19

为什么存在 NotImplementedException?

NotImplementedException 是一个很好的方式来说明一些事情还没有准备好。为什么它还没有准备好是方法作者的一个单独问题。在生产代码中你不太可能捕捉到这个异常,但如果你捕捉到了,你可以立即看到发生了什么,这比试图弄清楚为什么调用方法但什么也没发生甚至更糟要好得多——接收一些“临时”结果并得到“有趣”的副作用。

NotImplementedException 是 Java 的 UnsupportedOperationException 的 C# 等价物吗?

不,.NET 有 NotSupportedException

我必须运行我的算法,捕捉 NotImplementedExceptions 和一些如何将我的应用程序回滚到某个理智状态

好的 API 具有描述可能异常的 XML 方法文档。

我也很怀疑 NotSupportedException ......不支持?什么?如果不支持,为什么它是您界面的一部分?

可能有数百万个原因。例如,您可以引入新版本的 API 并且不想/不能支持旧方法。同样,最好查看描述性异常,而不是深入研究文档或调试第 3 方代码。

于 2009-01-04T09:36:19.297 回答
12

NotImplementedException 异常的主要用途是在生成的存根代码中:这样您就不会忘记实现它!例如,Visual Studio 将显式实现接口的方法/属性,其中主体抛出 NotImplementedException。

于 2009-01-04T09:30:30.430 回答
8

Re NotImplementedException- 这有一些用途;它提供了一个例外,(例如)您的单元测试可以锁定不完整的工作。而且,它确实做到了所说的:这根本不存在(还)。例如,对于 MS 库中存在但尚未编写的方法,“mono”会到处乱扔。

重新NotSupportedException- 并非一切都可用。例如,许多接口都支持一对“你能做到吗?” / “做这个”。如果“你能做到吗?” 返回 false,“这样做”抛出 是完全合理的NotSupportedException。示例可能是IBindingList.SupportsSearching/IBindingList.Find()等。

于 2009-01-04T09:32:39.723 回答
6

Microsoft 的大多数开发人员都熟悉适合 NotImplementedException 的设计模式。这实际上相当普遍。

一个很好的例子是复合模式,其中许多对象可以被视为一个对象的单个实例。组件用作(正确)继承的叶类的基本抽象类。例如,文件和目录类可能继承自同一个抽象基类,因为它们是非常相似的类型。这样,它们可以被视为单个对象(当您考虑文件和目录是什么时,这是有道理的——例如,在 Unix 中,一切都是文件)。

所以在这个例子中,Directory 类会有一个 GetFiles() 方法,但是 File 类不会实现这个方法,因为这样做没有意义。相反,您会得到一个 NotImplementedException ,因为 File 不像 Directory 那样有子级。

请注意,这不仅限于 .NET - 您会在许多 OO 语言和平台中遇到这种模式。

于 2009-01-04T09:34:39.843 回答
5

为什么你觉得有必要捕捉每一个可能的异常?你是否也包装了每个方法调用catch (NullReferenceException ex)

存根代码抛出NotImplementedException是一个占位符,如果它让它释放它应该是错误,就像NullReferenceException.

于 2009-01-04T10:39:27.110 回答
5

我认为 MS 在框架中添加 NotImplementedException 的原因有很多:

  • 为方便起见;既然许多开发人员在开发过程中都会需要它,为什么每个人都必须自己动手呢?
  • 这样工具就可以依赖它的存在;例如,Visual Studio 的“实现接口”命令生成抛出 NotImplementedException 的方法存根。如果它不在框架中,这是不可能的,或者至少相当尴尬(例如,它可能会生成在您添加自己的 NotImplementedException 之前无法编译的代码)
  • 鼓励一致的“标准做法”

Frankodwyer 将 NotImplementedException 视为潜在的定时炸弹。我会说任何未完成的代码都是定时炸弹,但 NotImplementedException 比替代方案更容易解除。例如,您可以让构建服务器扫描该类的所有用途的源代码,并将它们报告为警告。如果你想真正禁止它,你甚至可以在你的源代码控制系统中添加一个预提交钩子,以防止签入此类代码。

当然,如果您推出自己的 NotImplementedException,您可以将其从最终构建中移除,以确保不留下任何定时炸弹。但这只有在您在整个团队中始终如一地使用自己的实现时才有效,并且您必须确保在发布之前不要忘记将其删除。此外,您可能会发现无法删除它;也许有一些可接受的用途,例如测试未交付给客户的代码。

于 2009-01-04T12:00:32.170 回答
5

实际上没有理由实际捕获NotImplementedException。当被击中时,它应该会杀死你的应用程序,并且这样做非常痛苦。修复它的唯一方法不是捕获它,而是更改源代码(实现被调用方法或更改调用代码)。

于 2009-01-04T23:15:04.557 回答
4

两个原因:

  1. 方法在开发过程中被存根,并抛出异常以提醒开发人员他们的代码编写尚未完成。

  2. 实现一个子类接口,按照设计,它不实现继承的基类或接口的一个或多个方法。(有些接口太笼统了。)

于 2010-01-13T23:35:26.253 回答
3

对我来说,这听起来像是一个潜在的雷区。在遥远的过去,我曾经在一个遗留网络系统上工作,该系统已经连续运行多年,一天就崩溃了。当我们追查问题时,我们发现一些代码显然还没有完成并且永远不会工作 - 从字面上看,就像程序员在编码过程中被打断了一样。很明显,这个特定的代码路径以前从未被采用过。

墨菲定律说类似的事情只是乞求在 NotImplementedException 的情况下发生。在 TDD 等的这些日子里,它应该在发布之前被拾起,至少你可以在发布之前为那个异常进行 grep 代码,但仍然如此。

在测试时,很难保证覆盖所有情况,这听起来会使您的工作变得更加困难,因为运行时问题可能是编译时问题。(我认为类似的“技术债务”伴随着严重依赖“鸭子打字”的系统,虽然我承认它们非常有用)。

于 2009-01-04T10:24:50.273 回答
3

COM 互操作需要此异常。它是 E_NOTIMPL。链接的博客还显示了其他原因

于 2009-01-05T15:53:19.643 回答
3

抛出NotImplementedException是 IDE 生成编译存根代码的最合乎逻辑的方式。就像你扩展接口并让 Visual Studio 为你存根一样。

如果你做了一点 C++/COM,那也存在,除了它被称为E_NOTIMPL.

它有一个有效的用例。如果您正在处理接口的特定方法,您希望您的代码能够编译,以便您可以调试和测试它。根据您的逻辑,您需要从接口中删除该方法并注释掉非编译存根代码。这是一种非常原教旨主义的方法,虽然它有优点,但并不是每个人都会或应该坚持这一点。此外,大多数时候您希望界面是完整的。

拥有 NotImplementedException 可以很好地识别哪些方法尚未准备好,归根结底,很容易Ctrl+Shift+F找到它们,我也确信静态代码分析工具也会选择它。

您不打算发布有NotImplementedException异常的代码。如果您认为不使用它可以使您的代码变得更好,请继续前进,但是您可以做更多富有成效的事情来提高源代码质量。

于 2010-01-13T22:59:36.403 回答
2

未实现异常

当请求的方法或操作未实现时抛出异常。

将此作为 .NET 核心中定义的单个异常可以更轻松地找到和消除它们。如果每个开发人员都应该创建自己的开发人员ACME.EmaNymton.NotImplementedException,那么很难找到所有开发人员。

不支持异常

当调用的方法不受支持时,将引发异常。

例如,当尝试读取、查找或写入不支持调用功能的流时。

例如生成的迭代器(使用yield关键字) is-a IEnumerator,但IEnumerator.Reset方法 throws NotSupportedException

于 2009-01-04T11:29:11.830 回答
2

从 ECMA-335,CLI 规范,特别是 CLI 库类型,System.NotImplementedException,备注部分:

“本标准其他地方指定的许多类型和构造对于仅符合内核配置文件的 CLI 实现不是必需的。例如,浮点功能集由浮点数据类型 System.Single 和System.Double。如果实现中省略了对这些的支持,则任何引用包含浮点数据类型的签名的尝试都会导致 System.NotImplementedException 类型的异常。

因此,该例外适用于仅实现最低一致性配置文件的实现。最低要求的配置文件是内核配置文件(参见 ECMA-335 第 4 版 - 第 IV 部分,第 3 节),其中包括 BCL,这就是为什么异常包含在“核心 API”中,而不是其他位置的原因。

使用异常来表示存根方法,或者设计人员生成的方法缺乏实现是误解了异常的意图。

至于为什么此信息未包含在 MS 的 CLI 实现的 MSDN 文档中,我无法理解。

于 2009-07-30T15:39:34.187 回答
2

我不能保证 NotImplementedException(我基本同意你的观点),但我在工作中使用的核心库中广泛使用了 NotSupportedException。例如,DatabaseController 允许您创建任何受支持类型的数据库,然后在整个代码的其余部分中使用 DatabaseController 类,而无需过多关心下面的数据库类型。相当基本的东西,对吧?NotSupportedException 派上用场的地方(如果不存在,我会使用我自己的实现)是两个主要实例:

1) 将应用程序迁移到不同的数据库 人们通常认为这种情况很少发生或不需要。胡说八道。多出去走走。

2) 相同的数据库,不同的驱动程序 最近的例子是当一个使用 Access 支持的应用程序的客户端从 WinXP 升级到 Win7 x64 时。由于没有 64 位 JET 驱动程序,他们的 IT 人员安装了 AccessDatabaseEngine。当我们的应用程序崩溃时,我们可以很容易地从日志中看到它是 DB.Connect 崩溃并出现 NotSupportedException - 我们很快就能够解决这个问题。另一个最近的例子是我们的一个程序员试图在 Access 数据库上使用事务。尽管 Access 支持事务,但我们的库不支持 Access 事务(出于本文范围之外的原因)。NotSupportedException,现在是你大放异彩的时候了!

3) 通用函数我想不出一个简洁的“经验”示例,但如果您考虑类似向电子邮件添加附件的函数,您希望它能够获取一些常见文件,如 JPEG,从各种流类派生的任何东西,以及几乎任何具有“.ToString”方法的东西。对于后一部分,您当然不能考虑所有可能的类型,因此您将其设为通用。当用户传递 OurCrazyDataTypeForContainingProprietarySpreadsheetData 时,使用反射来测试是否存在 ToString 方法并返回 NotSupportedException 以指示不支持不支持 ToString 的所述数据类型。

NotSupportedException 绝不是一个关键功能,但我发现自己在处理大型项目时使用得更多。

于 2010-01-13T22:46:13.550 回答
2

假设您的生产代码中有这种方法

public void DoSomething()

如果你想让它稍后完成,你会拿哪一个?

public void DoSomething()
{
}

或者

public void DoSomething()
{
    throw new NotImplementedException();
}

我当然会选择第二个。再加上 Elmah 或您拥有的任何错误记录机制(作为整个应用程序的一个方面实现)。与日志/异常过滤一起,在被捕获时触发严重错误电子邮件通知。

NotImplementedException == unfinished 的论点也不正确。(1) 捕获未实现的方法应该留给单元测试/集成测试。如果你有 100% 的覆盖率(你现在应该这样做,有这么多模拟/存根/代码生成工具)没有 NotImplementedException,你担心什么?(2) 代码生成。简单明了。同样,如果我生成代码,并且只使用生成的代码的一半,为什么在生成的存根的其余部分中没有 NotImplementedException?

这就像说代码不应该编译,除非每个可为空的输入都应该检查/处理是否为空。(AKA 万亿美元的错误,如果不是更多的话)。语言应该是灵活的,而测试/合同应该是可靠的。

于 2011-08-23T08:03:04.430 回答
1

NotImplementedException 为 .NET 的某些方法引发(请参阅代码 DOM 中未实现的解析器 C#,但该方法存在!)您可以使用此方法 Microsoft.CSharp.CSharpCodeProvider.Parse 进行验证

于 2009-01-04T09:35:13.277 回答
1

我很少将它用于接口修复。假设您有一个需要遵守的接口,但某些方法永远不会被任何人调用,所以只需坚持一个 NotImplementedException,如果有人调用它,他们就会知道他们做错了什么。

于 2009-01-04T10:38:40.323 回答
1

它们都是针对两个常见问题的技巧。

NotImplementedException 是一种解决方法,适用于架构宇航员并喜欢先写下 API,然后再编写代码的开发人员。显然,由于这不是一个增量过程,因此您无法一次全部实现,因此您想通过抛出 NotImplementedException 来假装您已经完成了一半。

NotSupportedException 是对 C# 和 Java 等类型系统限制的一种破解。在这些类型系统中,如果 Rectangle 继承了所有 Shapes 特征(包括成员函数 + 变量),则您说 Rectangle 是一个 Shape。然而,在实践中,情况并非如此。例如,Square 是 Rectangle,但 Square 是 Rectangle 的限制,而不是概括。

因此,当您想要继承和限制父类的行为时,您可以在对限制没有意义的方法上抛出 NotSupported。

于 2009-01-04T15:24:46.690 回答
0

原型或未完成的项目呢?

我认为使用异常并不是一个非常糟糕的主意(尽管在这种情况下我使用了消息框)。

于 2009-01-04T09:24:33.173 回答
0

嗯,我有点同意。如果一个接口的制作方式并非所有类都可以实现它的所有部分,那么我认为它应该被分解。

如果 IList 可以或不能被修改,它应该被分解成两个,一个用于不可修改的部分(getter、lookup 等),一个用于可修改的部分(setter、add、remove 等)。

于 2009-01-04T09:38:32.787 回答
0

我的代码中有一些 NotImplementedExceptions。通常它来自接口或抽象类的一部分。我觉得我将来可能需要一些方法,它们作为课程的一部分是有意义的,但我只是不想花时间添加,除非我真的需要它。例如,我为游戏中的所有单独类型的统计数据提供了一个界面。其中一种是 ModStat,它是基本属性加上所有修饰符(即武器、盔甲、法术)的总和。我的 stat 接口有一个 OnChanged 事件,但我的 ModStat 通过计算它每次调用时引用的所有统计信息的总和来工作。因此,每次统计数据更改时都会引发大量 ModStat.OnChange 事件的开销,如果有人尝试添加/删除 OnChange 的侦听器,我只会抛出 NotImplementedException。

.NET 语言都是关于生产力的,那么为什么要花时间编写一些你甚至不会使用的东西呢?

于 2009-01-04T11:05:51.703 回答
0

这是一个例子:在Java中,每当你实现接口时Iterator,你必须重写明显的方法hasNext()next(),但也有delete()。在我拥有的 99% 的用例中,我不需要这个,所以我只是抛出一个NotImplementedException. 这比默默地什么都不做要好得多。

于 2009-01-04T14:46:44.583 回答
0

NotImplementedException 的存在只是为了促进开发。想象一下,您开始实现一个接口。您希望至少能够在完成一种方法的实现后进行构建,然后再进行下一种方法。使用 NotImplementedExceptions 对 NotImplemented 方法进行存根是一种很好的方式来处理未完成的代码,以后非常容易发现它。否则,您将冒着快速实施某些您可能忘记修复的风险。

于 2010-12-15T07:29:11.530 回答
-1

如果您不想使用它,请忽略它。如果你有一个代码块,它的成功取决于它的每一部分是否成功,但它可能会在两者之间失败,那么你唯一的选择是捕获基础Exception并回滚需要回滚的内容。忘了NotImplementedException。可能会抛出大量异常,例如MyRandomExceptionandGtfoExceptionOmgLolException。在您最初编写代码后,我可以从您正在调用的 API 中抛出另一个异常类型。编写代码时不存在的一种。处理你知道如何处理的那些,并为任何其他人回滚,即catch(Exception). 这很简单,我想......我发现它也很方便。尤其是当您尝试使用偶尔会强迫您做一些事情的语言/框架来做一些花哨的事情时。

我的一个例子是序列化。我在我的 .NET 库中添加了数据库中不存在的属性(例如,对现有“哑”属性的方便包装,例如FullName组合FirstNameMiddleName和的属性LastName)。然后我想将这些数据类型序列化为 XML 以通过网络发送它们(例如,从 ASP.NET 应用程序发送到 JavaScript),但序列化框架仅使用getset访问器序列化公共属性。我不希望你能够设置FullName,因为那样我必须解析它,并且可能有一些我错误地解析的不可预见的格式并且数据完整性超出了窗口。只使用底层属性更容易,但由于语言和 API 要求我有一个set访问器,我会抛出一个NotImplementedException(直到我阅读这个线程我才知道NotSupportedException,但任何一个都有效),这样如果某个程序员在路上确实尝试设置FullName,他会在测试期间遇到异常并意识到他的错误。

于 2010-06-25T14:22:41.857 回答