这真的非常让我感到高兴,所以我希望有人可以给我一个合理的理由来解释为什么事情就像他们一样。
NotImplementedException。你拉我的腿吧?
不,我不打算通过说,"坚持,方法实现 - 它会抛出NotImplementedException"。是的,没错,你必须实现抛出NotImplementedException的方法(不像C ++中的纯虚函数调用 - 现在才有意义!)。虽然这很可笑,但在我看来还有一个更严重的问题。
我只是想知道,在存在NotImplementedException的情况下,任何人都可以用.Net做任何事情?您是否希望使用try catch块包装每个抽象方法调用以防止可能未实现的方法?如果你遇到这样的例外,你应该怎么做呢?
我认为无法在不调用方法的情况下测试方法是否实际实现。因为调用它可能有副作用,我不能提前完成所有检查,然后运行我的算法。我必须运行我的算法,捕获NotImplementedExceptions以及一些如何将我的应用程序回滚到一些理智的状态。
这很疯狂。狂。疯。所以问题是:为什么存在NotImplementedException?
作为先发制人的罢工,我不希望任何人回应,"因为设计师需要将其放入自动生成的代码中。"这太可怕了。在您提供实现之前,我宁愿不编译自动生成的代码。例如,自动生成的实现可能是"throw NotImplementedException;"其中未定义NotImplementedException!
有没有人捕获并处理过NotImplementedException?你有没有在你的代码中留下NotImplementedException?如果是这样,这是代表定时炸弹(即,你不小心将它留在那里),还是设计缺陷(该方法不应该实施,永远不会被调用)?
我也非常怀疑NotSupportedException ...不支持?什么?如果它不受支持,为什么它是你的界面的一部分?微软的任何人都可以拼写不正当的继承吗?但是如果我对这个问题没有太多的滥用,我可能会提出另一个问题。
附加信息:
这是一个有趣的读物。
似乎与Brad Abrams有一个强烈的一致意见,"NotImplementedException是针对尚未实现的功能,但实际上应该(并且将会是)。类似于在构建类时可能开始的内容,获取所有方法抛出NotImplementedException,然后用实际代码刷新它们......"
来自Jared Parsons的注释非常弱,应该可以忽略:NotImplementedException:当类型由于任何其他原因没有实现方法时抛出此异常。
MSDN在这个主题上甚至更弱,只是声明"未实现所请求的方法或操作时抛出的异常"。
-
+1提出有关值得关注的模糊问题的有效问题。
-
我是否会获得徽章以及Rant标签?
-
我只能猜测你从来没有实现过具有给定接口的库...
-
好猜,但你不对。 我只能猜你不明白这个问题。
-
@Daniel - 还有更多的词源答案。 在Win32 C API中,定义了E_NOTIMPL返回值。 每个这样的返回值都映射到.Net异常。
-
@Heath - 所以你作为系统异常存在的NotImplementedException的推理是因为我们过去的罪过吗?
有一种情况我发现它很有用: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.dll和Microsoft.Cci.dll
-
将元数据放在名为DesignRules.xml的文件中,并将其作为嵌入式资源添加到程序集中
-
命名程序集Bevonn.CodeAnalysis。如果要对元数据或汇编文件使用不同的名称,请确保相应地将第二个参数更改为基本构造函数。
然后只需将生成的程序集添加到您的FxCop规则中,并从您的宝贵代码中取出那些该死的异常。有一些极端情况下,当一个人被抛出时它不会报告NotImplementedException但是如果你真的在编写这样的cthulhian代码,我真的认为你没有希望。对于正常使用,即throw new NotImplementedException();,它起作用,这就是最重要的。
-
我完全同意。在任何其他设置中抛出此信息就像在您的网页上显示"正在建设中"标志。
-
尼斯。但是,为什么NotImplementedException是.net核心的一部分?我更喜欢这个概念是特定于供应商的,我会在所有公共发布版本中有条件地编译出我的NotImplementedException。这不是.net核心的概念。
-
是什么原因导致它受损?对我来说,它为我节省了写一个愚蠢的例外的时间。用我的方法,我不需要编译出来。我可以保证它永远不会在发布代码中使用。它肯定不会使框架更大......
-
好吧,我告诉你,它是一个例外可能不是最好的解决方案。我更喜欢方面的想法。
-
@Martinho:是的宝贝,你来了。
-
@Martinho:意味着要补充一点,因为它在核心中所造成的损害是你不能从你的构建中删除它所以有可能运送产品或仍然有这些讨厌的bugger的补丁。我更喜欢一种不可能犯这样错误的技巧。
-
@Daniel:您可以通过实现该功能将其从代码中删除。我没有看到混乱所在的地方(请原谅我,如果这听起来很讽刺,那就不应该。)
-
@Daniel:将NotImplemented功能与TDD相结合,以确保实现功能;实现该功能后,删除该异常。它永远不会释放代码。 NotSupportedException适用于提供程序,并且有详细记录,并且应该被客户端捕获。
-
@Mike:如果它从未在已发布的代码中使用过,为什么它在.net核心中呢?如果它永远不会进入已发布的代码,则不需要记录,也不需要被任何人捕获。 PS:你的语气跟我一样好!在300个字符中很难保持柔软和柔软。
-
@Daniell Paull在.net核心中拥有它非常好,因为它提供了一种标准化的,未经形成的方式,我可以在我的IDE中"找到用法"来识别未完成的东西。
-
@krosenvold:IDE功能决定平台设计?听起来倒退了。
-
@krosenvold:您可以使用XYZException"查找用法"。它的工作原理是一样的。您只需要自己编写XYZException。
-
我已经接受这个作为"正确"的答案,因为我同意这种例外的使用。它的使用对我来说似乎仍然很危险,但通过正确的检查和平衡,这种用法是合理的。
-
我喜欢使用FxCop来仔细检查没有NotImplementedExceptions的想法。我用它反对我的一个项目,发现一个小错误。在VisitThrow中,您需要检查throwInstruction.Expression是否为null。每当有人通过裸露的"抛出"重新抛出时就会发生这种情况。指令。除此之外,非常感谢!
-
@Grhm感谢您的注意。
-
+1对于cthulhian代码。最好的形容词我听说过糟糕的代码。
-
我希望看到未实现的方法返回任意值。如果您的测试通过,那么您的测试是错误的。
-
可悲的是,我最近在Cthulhian代码中深陷其中。没有NotImplementedExceptions,我知道 - 但我更喜欢一个不存在的函数,只存在触手拥抱。
它支持一个相当常见的用例,一个有效但只能部分完成的API。假设我希望开发人员测试和评估我的API - WashDishes()至少在我的机器上工作,但我还没有开始编码DryDishes(),更不用说PutAwayDishes()了。而不是默默地失败,或者给出一些神秘的错误信息,我可以非常清楚为什么DryDishes()不起作用 - 我还没有实现它。
它的姐妹例外NotSupportedException主要适用于提供者模型。许多洗碗机具有烘干功能,因此属于界面,但我的折扣洗碗机不支持它。我可以通过NotSupportedException告知它
-
但为什么它是一个系统例外?为什么你不能抛出自己的例外?为什么MS成为.net核心的这一部分?对于没有干燥功能的洗碗机 - 为什么干燥方法?遗产不当。
-
为什么你宁愿为这种情况写你的例外?
-
因为那样我就可以从发布版本中完全删除它,以避免在代码中意外丢失。
-
但你仍然可以自由地这样做;您不必被迫使用NotSupportedException。尽管如此,情况经常出现,因此它应该在框架中存在标准类。
-
更正:NotImplementedException,但NotSupportedException同样适用。
-
当所有标准工具自动生成具有抛出NotImplementedException的存根的代码时,您就没有这样的自由!如果第三方程序集使用它,你也会陷入困境。
-
您可以在IDE中使用"find usages"来获取notimplementedexception。这就是为什么
-
@krosenvold:那是为什么? IDE功能决定平台设计?听起来倒退了。
-
@Daniel Paull:NotImplementedException是核心的一部分,因为人们很懒,如果它不存在,他们会使用Exception,NotSupportedException,InvalidOperationException或其他一些异常。
-
它也是核心的一部分,因为它对需要你实现某些东西的代码生成器很有用(比如visual studio自动接口实现)。此外,如果它意外地投入生产,这意味着您不小心忘记实现某些内容,您没有针对该代码路径进行测试,并且您有一个错误。
我将在一个地方总结我对此的看法,因为它们分散在几条评论中:
使用NotImplementedException表示接口成员尚未实现,但将是。您可以将其与自动化单元测试或QA测试结合起来,以确定仍需要实施的功能。
实现该功能后,删除NotImplementedException。为该功能编写了新的单元测试,以确保其正常工作。
NotSupportedException通常用于不支持对特定类型没有意义的功能的提供程序。在这些情况下,特定类型抛出异常,客户端捕获它们并在适当时处理它们。
框架中存在NotImplementedException和NotSupportedException的原因很简单:导致它们的情况很常见,因此在框架中定义它们是有意义的,这样开发人员就不必继续重新定义它们。此外,它使客户端更容易知道要捕获的异常(特别是在单元测试的上下文中)。如果你必须定义自己的异常,他们必须找出要捕获的异常,这至少是一个适得其反的时间接收器,并且经常是不正确的。
-
您承认将删除NotImplementedException - 如果是这种情况,则.net核心中不需要它。这是您自己私人开发过程中的私人问题。
-
你没看过第4点吗?
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,并且不希望/不能支持旧方法。再次,看到描述性异常而不是深入研究文档或调试第三方代码要好得多。
-
谢谢aku - API支持 - 弃用方法可能是此异常的合理原因。 Thouggh我更愿意看到DeprecatedMethodException。
-
有一个Obsolete属性,我希望我们有类似NotImplemented属性的东西来获得编译时警告。
-
@aku:优秀评论 - 这解决了在调用方法之前无法判断方法是否实际实现的问题(并非人们应该这样做)。
-
即使我经常使用异常(请参阅我的回答),我宁愿喜欢该属性,特别是如果我可以将其转换为抛出异常的方面(为了清楚起见,NotImplemented是我的第一选择)。
-
Java UnsupportedOperationException也可能是.NET InvalidOperationException。
-
NotImplemented属性仅在类直接(并且不使用接口调用方法)时才起作用
-
.Net的InvalidOperationException更接近Java的IllegalStateException。
-
@aku - 是的,伙伴。这就是他们在Java中所拥有的东西。 Java-devs可以调用静态方法"Trace",就像这个NotImplementedException.Trace("warning !! not implemented !!");我羡慕javists
NotImplementedException异常的主要用途是生成的存根代码:这样你就不会忘记实现它!例如,Visual Studio将显式实现接口的方法/属性,并且主体抛出NotImplementedException。
-
所以你不要忘记?是什么提醒?
-
@Daniel Paull:被抛出的例外!
-
@Mitch:假设你调用了代码。不是每个人都使用代码覆盖工具,因此很容易错过。
-
您最好希望该方法抛出异常,而不是简单地"无所事事",因为它没有实现。这样你就有机会找到并解决问题。
-
@Daniel Paull:断言遭遇同样的问题。
-
@Mitch:断言是一种测试系统中不能(按设计)发生的条件的工具 - 任何被触发的断言都表示编程错误。异常表示运行时环境不一定(必然)表示编程错误。苹果和橘子。
-
您可以做的是搜索NotImplementedExceptions的所有代码,并确保在发布时找到零。
Re NotImplementedException - 这有一些用途;它提供了一个例外(例如)您的单元测试可以锁定不完整的工作。但是,它确实做了所说的:这根本不存在(还)。例如,对于MS库中存在的方法,"mono"会将其全部抛出,但尚未编写。
Re NotSupportedException - 并非所有东西都可用。例如,许多接口都支持一对"你可以这样做吗?" /"做这个"。如果"你能做到吗?"返回false,"执行此操作"抛出NotSupportedException是完全合理的。示例可能是IBindingList.SupportsSearching / IBindingList.Find()等。
-
好的,所以应该有一个Mone.NotImplementedException - 为什么它是.net核心的一部分?如果你避免不正确的继承,那么它就是一个对象,所以"你能做到吗?" /"这样做"是多余的。
-
我不确定我是否完全同意这是不正当的继承,但我们去......
-
它确实闻起来像是不正当的继承。
Microsoft的大多数开发人员都熟悉NotImplementedException适合的设计模式。这实际上相当普遍。
一个很好的例子是复合模式,其中许多对象可以被视为对象的单个实例。组件用作(正确)继承的叶类的基本抽象类。例如,File和Directory类可以从相同的抽象基类继承,因为它们是非常相似的类型。这样,它们可以被视为一个单独的对象(当你考虑什么文件和目录时这是有意义的 - 例如在Unix中,一切都是文件)。
因此,在此示例中,Directory类将有一个GetFiles()方法,但是,File类不会实现此方法,因为这样做没有意义。相反,您会收到NotImplementedException,因为File没有像Directory那样生成子项。
请注意,这不仅限于.NET - 您将在许多OO语言和平台中遇到此模式。
-
当通过抽象接口询问它的子节点时,为什么文件不会返回空列表?
-
作为一名应用程序开发人员,我当然不会指望它。如何通过Directory类实现一个名为AddFile()的方法 - 如果在File类上调用它,您会期望什么?我期待一个例外。这种行为应该在一个好的API中保持一致。
-
文件不是目录 - 不正确的继承。需要仔细设计可变的抽象接口。
-
-1,如果子类无法实现GetFiles,则层次结构错误,en.wikipedia.org / wiki / Liskov_substitution_principle
-
@orip:你这个男人。观察理性的声音。
-
在我的示例中,文件不从Directory继承;你误解了它。对不起,这并不违反Liskov Substitution原则。基本抽象组件类抛出异常。由子类来适当地实现这些方法。
-
可能是比较差的类比,但贾维斯的观点是正确的。我更喜欢查看未实现的复合方法,但不是。您经常会通过虚拟方法看到这一点。
-
这与其他答案中流行的观点相反,即生产代码不应该抛出NotImplementedExceptions。我无法理解"可以做但不做"的意思。如果你说"会做,但还没有",那么我再次开心 - 看到接受的答案。
-
Microsoft在Microsoft Office对象模型中非常广泛地使用NotImplementedException。作为一名PowerPoint程序员,它有时会让我发疯!例如,当我尝试调用Shape或AnimationSettings的某些方法或属性时,它们可能正常工作或者可能会根据情况抛出NotImplementedException。我希望有一种方法可以检查NotImplementedException的方法,而不会实际导致它。
我认为MS为框架添加NotImplementedException有很多原因:
-
为方便起见;由于许多开发人员在开发过程中需要它,为什么每个人都必须自己动手?
-
因此工具可以依赖它的存在;例如,Visual Studio的"实现接口"命令生成抛出NotImplementedException的方法存根。如果它不在框架中,这是不可能的,或者至少相当尴尬(例如,它可以生成在您添加自己的NotImplementedException之前不编译的代码)
-
鼓励一致的"标准做法"
Frankodwyer认为NotImplementedException是一种潜在的定时炸弹。我会说任何未完成的代码都是一个定时炸弹,但NotImplementedException比替代方案更容易解除武装。例如,您可以让构建服务器扫描此类的所有用途的源代码,并将其报告为警告。如果你想真正禁止它,你甚至可以在源代码控制系统中添加一个预提交钩子,以防止签入此类代码。
当然,如果你滚动自己的NotImplementedException,你可以从最终版本中删除它,以确保没有留下时间炸弹。但是,只有在整个团队中始终如一地使用自己的实现时,这才有效,并且您必须确保在发布之前不要忘记将其删除。此外,您可能会发现无法将其删除;也许有一些可接受的用途,例如测试未发送给客户的代码。
-
为什么没有"Microsoft.Development.Support"或类似的程序集,它具有有用的类和实用程序(包括NotImplementedException)。目的是在开发版本中引用该程序集,但不在已发布的代码中引用该程序集?
-
这需要将Microsoft.Development.Support程序集引用添加到正在使用它的每个项目,这给工具带来了另一个不便和障碍。
-
这个问题是什么?我宁愿让我的系统正确,这给我的开发人员带来了轻微的不便。使用DLL会更方便,但是有利于成本(通过成本我的意思是努力和复杂性)。
实际上没有理由捕获NotImplementedException。当被击中时,它应该杀死你的应用程序,并且这样做非常痛苦。修复它的唯一方法不是捕获它,而是更改源代码(实现被调用的方法或更改调用代码)。
-
这是一个非常奇怪的答案。如果没有人想要捕获异常,为什么会抛出它,为什么它是.Net核心的一部分?注意:仅仅因为抛出异常并不意味着它不会被捕获。为什么不在调试和发布版本中断言(false)?
-
你会在catch子句中做什么?特别是对于NotImplementedException?
-
如果我按照自己的方式行事,我将永远不会抛出或捕获NotImplementedException。我试图找到一个案例,它是核心库的一部分。
-
还有许多其他例外你不应该抓住(StackOverflowException,OutOfMemoryException)
为什么你觉得需要抓住每一个可能的例外?你也用catch (NullReferenceException ex)包装每个方法调用吗?
抛出NotImplementedException的存根代码是一个占位符,如果它要释放它应该是像NullReferenceException那样的bug。
-
如果仅用作占位符,为什么NotImplementedException是.net核心的一部分?我更喜欢这个概念是特定于供应商的,我会在所有公共发布版本中有条件地编译出我的NotImplementedException。这不是.net核心的概念。
-
@Daniel,我想我们对它有不同的看法,但我绝对不会在发布版本中编译出异常,就像我不会捕获NullReferenceException一样 - 我会修复bug。
-
@orip:我觉得你误读了。编译出NotImplementedException类,而不是它所使用的位置。这将强制NotImplementedException的任何杂散使用成为编译错误。
-
@Daniel:啊,这很有道理。
两个原因:
方法在开发期间被删除,并抛出异常以提醒开发人员他们的代码编写没有完成。
实现子类接口,该接口在设计上不实现继承的基类或接口的一个或多个方法。 (有些界面太笼统了。)
-
如果您正在使用对于您正在编写的类"过于笼统"的接口,您应该考虑只有您实际需要的方法的接口。如果IFooBar有三个方法并且我有一个IFooBar实例,那么这些方法可以更好地实现(或至少只抛出合同定义的异常)。我会说95%的时间或更多时间抛出NotImplementedException()会违反Liskov Substitution Principle。
-
是的,但有时你没有选择,比如当你使用标准的.NET库时。考虑迭代器和I / O流类中的一些"可选"方法。
-
@S。 DePouw:如果有另一种方法来确定是否应该在运行时调用该方法,它可能不会违反Liskov替换原则,例如CanXXX()测试告诉您可以调用XXX()。但是,我对这些界面非常怀疑,因为它们闻起来像是不正确的插入 - 就像这个答案中的(2)一样。
抛出NotImplementedException是IDE生成编译存根代码的最合理方式。就像扩展界面并让Visual Studio为您存根一样。
如果你做了一些C ++ / COM,那里也存在,除了它被称为E_NOTIMPL。
有一个有效的用例。如果您正在处理接口的特定方法,则需要编译代码以便进行调试和测试。根据您的逻辑,您需要从接口中删除该方法并注释掉非编译存根代码。这是一种非常原教旨主义的方法,虽然它具有优点,但不是每个人都会或应该坚持这一点。此外,大多数时候您希望界面完整。
有一个NotImplementedException可以很好地识别哪些方法还没有准备好,在一天结束时就像按Ctrl+Shift+F找到它们一样简单,我也相信静态代码分析工具也会提取它。
您不打算发布具有NotImplementedException异常的代码。如果您认为不使用它可以使您的代码变得更好,那么您可以采取更有成效的措施来提高源代码质量。
-
"根据你的逻辑,你需要从界面上删除方法并注释掉非编译存根代码。"我不要把话放在嘴里 - 我绝不会建议你这样做。我同意你在开发过程中会有未实现的存根。但是那些存根可能会抛出你自己的异常。我只是不明白为什么NotImplementedException应该作为.Net核心库的一部分存在。
-
那么在这种情况下你做了一个普通的StackOverflow,并没有问你真正想问的是什么。你自己的答案非常简洁地回答了你的问题,但我,我相信,其他人把你的问题读作"你不应该为未实现的方法抛出异常"。
-
为什么在有标准的情况下抛出自己的异常?重新发明轮子是为什么在"我们的"行业中工作有时是如此痛苦(NIH综合症......)的主要原因之一。在某些地方你需要存根,并且抛出标准化的例外可以让你轻松地使用工具来跟踪这些比如"grep"。我也对TDD使用NIE很多。有很多地方他们有意义。刚离开存根有时甚至更危险,我喜欢用"// TODO:"垃圾邮件代码,因为一旦写下来我就忘了它。
COM interop需要这个例外。这是E_NOTIMPL。链接的博客还显示其他原因
-
如果唯一有效的用途是映射到COM的E_NOTIMPL,那么这个异常肯定应该在interop命名空间中。该博客还有哪些其他有效用途?它表明一种短暂的发展状态的论点很少;使用你自己的例外。
这对我来说听起来像是一个潜在的雷区。在遥远的过去,我曾经在一个多年来一直运行的遗留网络系统上工作过一天。当我们跟踪问题时,我们发现一些代码显然没有完成,哪些代码无法工作 - 字面意思是,程序员在编码过程中被打断了。很明显,这个特定的代码路径从未被采用过。
墨菲定律说,类似的东西只是在NotImplementedException的情况下发生。在TDD等的这些日子里,它应该在发布之前被选中,并且至少你可以在发布之前为该异常编写grep代码,但仍然如此。
在测试时很难保证每个案例的覆盖率,这听起来像是通过编写可能是编译时间问题的运行时问题而使您的工作更难。 (我认为类似的"技术债务"伴随着严重依赖'鸭子打字'的系统,而我承认它们非常有用)。
NotImplementedException
未实现请求的方法或操作时抛出异常。
将此作为.NET核心中定义的单个异常,可以更轻松地查找和根除它们。如果每个开发人员都应该创建自己的ACME.EmaNymton.NotImplementedException,那么很难找到所有这些。
NotSupportedException异常
不支持调用的方法时抛出异常。
例如,当尝试读取,搜索或写入不支持调用功能的流时。
例如,生成的迭代器(使用yield关键字)是-a IEnumerator,但IEnumerator.Reset方法抛出NotSupportedException。
-
"让它更容易找到并根除它们" - 更容易吗?那是你的理由吗?不是因为这是正确的方法,而是因为这是一种简单的方法。好一个人。
-
为什么比不支持随机访问的流有搜索方法?让我们不要躲在微软糟糕的流界面设计背后。
-
我现在害怕调用Reset()一个任何枚举器。
-
当然,一个INotResetEnumerator可能会更好,这是IEnumerator派生的。但导致课程太多。
-
设计它会很有趣。我不同意它会导致太多的接口。
-
这一切都取决于粒度,你可以有IListMutable(包含Add,Clear,Insert,Remove,RemoveAt)和IListImmutable(包含,CopyTo,GetEnumerator,IndexOf)。 MyList:IListMutable,IListImmutable {/ * impl * /}
-
但是如果你想要一个只添加和只读列表怎么办? MyList:IListAdd,IListInsert,IListImmutable {/ * impl * /}并让IListMutable:IListAdd,IListClear,IListInsert,IListRemove,IListRemoveAt
-
用于改变抽象类型的接口总是很棘手,应该非常仔细地设计。我们敢问一个正方形是否是一个矩形?
-
听起来你建议任何接口的设计者应该总是能够以oracle的方式猜测接口可能被放置的所有用途。当然,在一个项目中,这可能是可能的;总的来说,我宁愿不去思考。
-
@zacharyrsnow:如果遵循声音面向对象的设计原则,那么界面将是直观的并且用于明确的目的。由于目的很明确,在设计期间不需要超能力(如甲骨文)。所以,总的来说,我更愿意......
-
@dalle:正确的设计是让IEnumerable不提供重置,但是在更新集合时提供相当宽松的枚举行为保证(抛出异常将是一个选项,但不是必需的,如果整个枚举中存在所有项目只返回一次等); IMul??tipassEnumerable将添加Reset,FastCount和Count(FastCount将在其选项中返回-1或正确的计数;如果在完全枚举之前已知集合的大小,则可以使用它为列表预先分配空间) 。
假设您在生产代码中使用此方法
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万亿美元的错误,如果不是更多)。语言应该是灵活的,而测试/合同应该是可靠的。
-
更糟糕的是,像Point ComputeLocation() { return default(Point); }这样的东西。在许多情况下,没有返回值的方法的正确行为根本不执行任何操作(例如,许多类将IDisposable.Dispose()实现为无操作),但是使用返回结构类型的方法返回默认实例,就好像它一样真正的价值似乎真的很可疑。
从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,如果有人调用它,他们就会知道他们做错了什么。
-
一个永远不能被调用的接口方法?有没有搞错?
-
如果未实现整个接口,请不要实现它。使用这种方法,你应该使用可以类型化的语言,比如Python甚至c#4 :(。
-
所以你有10个类实现了一个接口,但其中一个或其中几个已经有你需要调用的额外方法。这样做的方法是什么?我用额外的方法实现接口,如果类型匹配则调用它。你有什么解决方案? 10个单独的课程?
-
@Slough:给我一个例子,我会免费修复你的设计。
-
IDataReader有很多方法。在知道我只需要三种方法之前,我必须实现它。接口位于.net框架中。我无法改变它。我也将它传递给我无法改变的框架类。这是一个糟糕的界面,但我坚持下去。
-
@Mike:我感觉到你的痛苦,这种情况可能是有效的,因为你的手被迫了。所以MS包含了这个不好的例外,以支持他们糟糕的界面和框架设计。合理的论点。我认为在这种情况下,两个错误让事情变得更糟。
-
IDataReader和朋友的情况表明使用。大多数实例仅使用其方法的子集。将接口构造为包装器时,未使用的存根需要执行某些操作。这是最明智的事情。
他们都是两个常见问题的黑客。
NotImplementedException是针对架构宇航员的开发人员的解决方法,并且喜欢先写下API,稍后编写代码。显然,由于这不是一个增量过程,你不能一次实现所有,因此你想通过抛出NotImplementedException假装你是半完成的。
NotSupportedException是对C#和Java中类型系统限制的破解。在这些类型的系统中,你说Rectangle'是'Shape iff Rectangle继承了Shapes的所有特性(包括成员函数+变量)。但是,在实践中,情况并非如此。例如,Square是一个Rectangle,但Square是Rectangle的限制,而不是泛化。
因此,当您想要继承和限制父类的行为时,您会在对限制没有意义的方法上抛出NotSupported。
-
没有理由为什么建筑宇航员不能设计合理的界面,除了 - 他们在设计上很糟糕并且无法完成他们的工作。架构师(yuk)不应该在API设计中拥有最后的发言权;在实施之前,应咨询开发人员并签字。
-
当您想要继承和限制父类的行为时,您将破坏基类/接口不变量。除非基类/接口的契约明确表示实现可能不支持所有功能(可能使用CanDo / Do()模式),否则不要这样做。您很可能会破坏客户端代码。如果你必须打破Rectangle的合约来创建一个Square类,Square就不是(即不继承)Rectangle。我知道很容易看到继承是"是一个",但有时这可能是一个非常糟糕的比喻。
-
@Afd - 我建议你做一些阅读。"正方形是一个特殊的矩形"是一个无处不在的例子,显示了人们遗传错误的地方。
对于.NET的某些方法,抛出NotImplementedException(请参阅Code DOM中的解析器C#,该解析器未实现,但该方法存在!)
您可以使用此方法验证Microsoft.CSharp.CSharpCodeProvider.Parse
-
当我意识到必须编写自己的解析器时,我曾经疯了。那真的很糟糕。
-
不只是"坏",而是不可原谅的。
-
看看这个列表:blogs.msdn.com/brada/archive/2004/07/29/201354.aspx#203896
原型或未完成的项目怎么样?
我认为使用异常并不是一个非常糟糕的主意(尽管在这种情况下我使用了一个消息框)。
-
那么,它永远不应该成为生产代码?那为什么它是.net核心的一部分?如果你想要这个概念,那么只需"抛出新的Object();"注释表明该方法正在等待实现。
-
顺便说一句 - 我会给你+1,因为你更喜欢使用异常以外的东西。我个人会掉入一个断言(假)和一个虚拟返回值,假设我需要编译代码。
-
@Daniel Paull:但肯定会更危险,因为断言不会被编译成Release代码?...
-
@Daniel Paull:事实上你已经慢慢记住了我的记忆,我可以回想起发生过这种情况的情况......
-
如果你按合同编程,测试你的系统并证明正确性,断言就是花花公子。替代方案似乎只是一个翅膀和一个祈祷。我是断言的大力倡导者,让你的设计正确。它不应该是让你的系统工作的运气。
下面是一个例子:在Java中,无论何时实现接口Iterator,都必须覆盖显而易见的方法hasNext()和next(),但也有delete()。在99%的用例中,我不需要这个,所以我只抛出一个NotImplementedException。这比默默地无所事事要好得多。
-
更好的是接口没有delete()方法。接口听起来像.net流一样破碎。
好吧,我有点同意。如果一个接口的制作方式并非所有类都可以实现它的所有部分,那么在我看来它应该被分解。
如果IList可以修改或不能修改,它应该被分解为两个,一个用于不可修改的部分(getter,lookup等),另一个用于可修改的部分(setter,add,remove等)。
NotImplementedException仅用于促进开发。想象一下,你开始实现一个接口。在移动到下一个方法之前,您希望能够在完成实现一个方法时至少构建。使用NotImplementedExceptions对NotImplemented方法进行存储是一种很好的生存未完成代码的方法,以后很容易发现。否则,您将面临快速实施可能忘记解决的问题的风险。
-
你可以自由地遵循自己喜欢的方法,这样你就可以"存根"你喜欢的任何东西。为什么微软会把它放在标准库中呢?您可以轻松定义自己的抛出异常。如果您使用自己的异常类,那么您可以从代码中删除类定义,编译器会告诉您是否忘记用实际实现替换其中一个存根 - 我比搜索模式更好文本...请注意,您无法从构建中删除NotImplementedException,因此如果您使用它,则不能选择此选项。
我的代码中有一些NotImplementedExceptions。通常它来自接口或抽象类的一部分。我认为将来可能需要的一些方法,它们作为课程的一部分是有意义的,但我只是不想花时间添加,除非我真的需要它。例如,我的游戏中有各种各样的统计数据的界面。其中一种是ModStat,它是基本属性加上所有修饰符(即武器,护甲,法术)的总和。我的stat接口有一个OnChanged事件,但是我的ModStat通过计算每次调用它引用的所有统计数据的总和来工作。因此,每次stat更改时都不会产生大量ModStat.OnChange事件的开销,如果有人试图向OnChange添加/删除监听器,我只会抛出NotImplementedException。
.NET语言都与生产力有关,那么为什么要花时间编写一些你甚至不会使用的东西呢?
-
"那么为什么要花时间编写你甚至不会使用的东西呢?"有趣的是 - 您似乎已经实现了可能永远不会实际实现的存根。按照你自己的建议,你不需要NotImplementedException!
-
丹尼尔说,你不应该写没有用的存根。如果您将来"可能"需要它们,请在将来编写它们。大多数时候,.NET程序集版本非常好。
-
"按照你自己的建议,你不需要NotImplementedException!"这里的例外点是,如果调用该方法,您仍然可以编译,并且知道您的代码失败的原因。如果你得到异常,添加代码 - 就这么简单。
-
为什么不抛出自己的异常类型?为什么这是一个.net核心概念?
如果你不想使用它,那就忽略它。如果你有一块代码块,其成功取决于它的每一块成功,但它可能在两者之间失败,那么你唯一的选择是捕获基础Exception并回滚需要回滚的内容。忘记NotImplementedException。可能会抛出大量异常,例如MyRandomException和GtfoException以及OmgLolException。在您最初编写代码之后,我可以从您调用的API中抛出另一个异常类型。编写代码时不存在的一个。处理你知道如何处理和回滚任何其他人,即catch(Exception)。这很简单,我想......我发现它也派上用场了。特别是当你用语言/框架尝试花哨的东西时偶尔强迫你。
我有一个例子是序列化。我在我的.NET库中添加了数据库中不存在的属性(例如,对现有"哑"属性的方便包装,例如组合FirstName,MiddleName和LastName的FullName属性)。然后我想将这些数据类型序列化为XML以通过网络发送它们(例如,从ASP.NET应用程序发送到JavaScript),但序列化框架仅使用get和set访问器序列化公共属性。我不希望你能够设置FullName因为那时我必须解析它,并且可能有一些无法预料的格式我错误地解析并且数据完整性消失了。为此只使用底层属性更容易,但由于语言和API要求我有一个set访问器,我会抛出NotImplementedException(我不知道NotSupportedException直到我读到这个帖子,但任何一个都有效)所以,如果一些程序员试图设置FullName他将在测试期间遇到异常并意识到他的错误。
-
catch(Exception),或者C ++中的类似catch(...)是你可以在编程中做的最邪恶的事情之一。它被称为"在地毯下扫地",并开始下降到泥球大球。将catch(Exception)放在任何地方并不简单也不方便 - 如果您认为是这样,那么在编写健壮的代码之前还有很长的路要走。在您的示例中,如果FullName被认为是不可变的(即,无法设置),为什么API有一个setter?你的(或微软)设计出了问题。有很多方法可以避免添加setter ...
-
catch(Exception)正是你想要的应用程序的"顶部",如果异常未被处理,则运行时必须处理它,这对于用户来说通常是丑陋和不愉快的。它本质上是一次崩溃。示例在Main内或Page_Load内。 FullName属性有一个setter,因为序列化API需要它,正如我在你评论的帖子中所说的那样......