关于.net:为什么存在NotImplementedException?

Why does NotImplementedException exist?

这真的非常让我感到高兴,所以我希望有人可以给我一个合理的理由来解释为什么事情就像他们一样。

NotImplementedException。你拉我的腿吧?

不,我不打算通过说,"坚持,方法实现 - 它会抛出NotImplementedException"。是的,没错,你必须实现抛出NotImplementedException的方法(不像C ++中的纯虚函数调用 - 现在才有意义!)。虽然这很可笑,但在我看来还有一个更严重的问题。

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

我认为无法在不调用方法的情况下测试方法是否实际实现。因为调用它可能有副作用,我不能提前完成所有检查,然后运行我的算法。我必须运行我的算法,捕获NotImplementedExceptions以及一些如何将我的应用程序回滚到一些理智的状态。

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

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

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

我也非常怀疑NotSupportedException ...不支持?什么?如果它不受支持,为什么它是你的界面的一部分?微软的任何人都可以拼写不正当的继承吗?但是如果我对这个问题没有太多的滥用,我可能会提出另一个问题。

附加信息:

这是一个有趣的读物。

似乎与Brad Abrams有一个强烈的一致意见,"NotImplementedException是针对尚未实现的功能,但实际上应该(并且将会是)。类似于在构建类时可能开始的内容,获取所有方法抛出NotImplementedException,然后用实际代码刷新它们......"

来自Jared Parsons的注释非常弱,应该可以忽略:NotImplementedException:当类型由于任何其他原因没有实现方法时抛出此异常。

MSDN在这个主题上甚至更弱,只是声明"未实现所请求的方法或操作时抛出的异常"。


有一种情况我发现它很有用:TDD。

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

由于我从未在任何其他情况下使用NotImplementedException,因此不会将NotImplementedException传递给发布代码,因为它总会使某些测试失败。

你不需要在整个地方捕获它。好的API记录抛出的异常。那些是你应该寻找的。

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

这是代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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);
        }
    }
}

这是规则元数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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();,它起作用,这就是最重要的。


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

它的姐妹例外NotSupportedException主要适用于提供者模型。许多洗碗机具有烘干功能,因此属于界面,但我的折扣洗碗机不支持它。我可以通过NotSupportedException告知它


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

  • 使用NotImplementedException表示接口成员尚未实现,但将是。您可以将其与自动化单元测试或QA测试结合起来,以确定仍需要实施的功能。

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

  • NotSupportedException通常用于不支持对特定类型没有意义的功能的提供程序。在这些情况下,特定类型抛出异常,客户端捕获它们并在适当时处理它们。

  • 框架中存在NotImplementedExceptionNotSupportedException的原因很简单:导致它们的情况很常见,因此在框架中定义它们是有意义的,这样开发人员就不必继续重新定义它们。此外,它使客户端更容易知道要捕获的异常(特别是在单元测试的上下文中)。如果你必须定义自己的异常,他们必须找出要捕获的异常,这至少是一个适得其反的时间接收器,并且经常是不正确的。


  • Why does the NotImplementedException
    exist?

    NotImplementedException是一个很好的方式来表示某些东西尚未准备好。为什么它没有准备好是方法作者的一个单独的问题。在生产代码中你不太可能捕获这个异常,但如果你这样做,你可以立即看到发生了什么,它比试图弄清楚为什么方法被调用但没有发生甚至更糟 - 获得一些"临时"结果并得到更好"有趣"的副作用。

    Is NotImplementedException the C#
    equivalent of Java's
    UnsupportedOperationException?

    不,.NET有NotSupportedException

    I have to run my algorithm, catch
    NotImplementedExceptions and the some
    how roll back my application to some
    sane state

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

    I'm very suspicious of the
    NotSupportedException also... Not
    supported? What the? If it's not
    supported, why is it part of your
    interface?

    可能有数百万个原因。例如,您可以引入新版本的API,并且不希望/不能支持旧方法。再次,看到描述性异常而不是深入研究文档或调试第三方代码要好得多。


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


    Re NotImplementedException - 这有一些用途;它提供了一个例外(例如)您的单元测试可以锁定不完整的工作。但是,它确实做了所说的:这根本不存在(还)。例如,对于MS库中存在的方法,"mono"会将其全部抛出,但尚未编写。

    Re NotSupportedException - 并非所有东西都可用。例如,许多接口都支持一对"你可以这样做吗?" /"做这个"。如果"你能做到吗?"返回false,"执行此操作"抛出NotSupportedException是完全合理的。示例可能是IBindingList.SupportsSearching / IBindingList.Find()等。


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

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

    因此,在此示例中,Directory类将有一个GetFiles()方法,但是,File类不会实现此方法,因为这样做没有意义。相反,您会收到NotImplementedException,因为File没有像Directory那样生成子项。

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


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

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

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

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


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


    为什么你觉得需要抓住每一个可能的例外?你也用catch (NullReferenceException ex)包装每个方法调用吗?

    抛出NotImplementedException的存根代码是一个占位符,如果它要释放它应该是像NullReferenceException那样的bug。


    两个原因:

  • 方法在开发期间被删除,并抛出异常以提醒开发人员他们的代码编写没有完成。

  • 实现子类接口,该接口在设计上不实现继承的基类或接口的一个或多个方法。 (有些界面太笼统了。)


  • 抛出NotImplementedException是IDE生成编译存根代码的最合理方式。就像扩展界面并让Visual Studio为您存根一样。

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

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

    有一个NotImplementedException可以很好地识别哪些方法还没有准备好,在一天结束时就像按Ctrl+Shift+F找到它们一样简单,我也相信静态代码分析工具也会提取它。

    您不打算发布具有NotImplementedException异常的代码。如果您认为不使用它可以使您的代码变得更好,那么您可以采取更有成效的措施来提高源代码质量。


    COM interop需要这个例外。这是E_NOTIMPL。链接的博客还显示其他原因


    这对我来说听起来像是一个潜在的雷区。在遥远的过去,我曾经在一个多年来一直运行的遗留网络系统上工作过一天。当我们跟踪问题时,我们发现一些代码显然没有完成,哪些代码无法工作 - 字面意思是,程序员在编码过程中被打断了。很明显,这个特定的代码路径从未被采用过。

    墨菲定律说,类似的东西只是在NotImplementedException的情况下发生。在TDD等的这些日子里,它应该在发布之前被选中,并且至少你可以在发布之前为该异常编写grep代码,但仍然如此。

    在测试时很难保证每个案例的覆盖率,这听起来像是通过编写可能是编译时间问题的运行时问题而使您的工作更难。 (我认为类似的"技术债务"伴随着严重依赖'鸭子打字'的系统,而我承认它们非常有用)。


    NotImplementedException

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

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

    NotSupportedException异常

    不支持调用的方法时抛出异常。

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

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


    假设您在生产代码中使用此方法

    1
    public void DoSomething()

    如果你想稍后离开它,你会选择哪一个?

    1
    2
    3
    public void DoSomething()
    {
    }

    要么

    1
    2
    3
    4
    public void DoSomething()
    {
        throw new NotImplementedException();
    }

    我肯定会采取第二个。与Elmah或您拥有的任何错误记录机制相结合(作为整个应用程序的一个方面实现)。与日志/异常过滤一起触发一个被捕获时的重大错误电子邮件通知。

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

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


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

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

    因此,该例外旨在实现仅实现最小一致性配置文件的实现。所需的最低配置文件是内核配置文件(参见ECMA-335第4版 - 第IV部分,第3节),其中包括BCL,这就是为什么例外包含在"核心API"中,而不是在其他某个位置。

    使用异常来表示存根方法,或者对于缺少实现的设计器生成的方法,就是误解了异常的意图。

    至于为什么MS的MSDN文档中没有包含这些信息,CLI的实现超出了我的范围。


    我不能保证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绝不是一个至关重要的功能,但是当我在大型项目上工作时,我发现自己使用了更多东西。


    我很少用它来修复界面。假设你有一个你需要遵守的界面,但任何人都不会调用某个方法,所以只要坚持一个NotImplementedException,如果有人调用它,他们就会知道他们做错了什么。


    他们都是两个常见问题的黑客。

    NotImplementedException是针对架构宇航员的开发人员的解决方法,并且喜欢先写下API,稍后编写代码。显然,由于这不是一个增量过程,你不能一次实现所有,因此你想通过抛出NotImplementedException假装你是半完成的。

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

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


    对于.NET的某些方法,抛出NotImplementedException(请参阅Code DOM中的解析器C#,该解析器未实现,但该方法存在!)
    您可以使用此方法验证Microsoft.CSharp.CSharpCodeProvider.Parse


    原型或未完成的项目怎么样?

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


    下面是一个例子:在Java中,无论何时实现接口Iterator,都必须覆盖显而易见的方法hasNext()next(),但也有delete()。在99%的用例中,我不需要这个,所以我只抛出一个NotImplementedException。这比默默地无所事事要好得多。


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

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


    NotImplementedException仅用于促进开发。想象一下,你开始实现一个接口。在移动到下一个方法之前,您希望能够在完成实现一个方法时至少构建。使用NotImplementedExceptions对NotImplemented方法进行存储是一种很好的生存未完成代码的方法,以后很容易发现。否则,您将面临快速实施可能忘记解决的问题的风险。


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

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


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

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