为什么NotImplementedException存在?
这真的真的让我害怕,所以我希望有人能给我一个合理的理由,说明为什么事情是这样的。
NotImplementedException。 你在拉我的腿,对吧?
不,我不打算用这个便宜的东西来说:“等等,方法实现了 – 抛出一个NotImplementedExceptionexception。” 是的,没错,你必须实现这个方法来抛出一个NotImplementedExceptionexception(不像C ++中的纯虚函数调用 – 现在这是有道理的!)。 虽然这很有趣,但我脑海中还有一个更严重的问题。
我只是想知道,如果存在NotImplementedException,那么任何人都可以使用.Net做任何事情? 你是否期望用try catch块封装每个抽象方法调用,以防止可能不会被实现的方法? 如果你发现这样的例外,你应该怎么处理呢?
我没有办法testing一个方法是否实际上没有调用它。 由于调用它可能有副作用,我不能做所有我的检查,然后运行我的algorithm。 我必须运行我的algorithm,赶上NotImplementedExceptions和一些如何回滚我的应用程序到一个理智的状态。
这很疯狂。 狂。 疯。 所以问题是: 为什么NotImplementedException存在 ?
作为一个先发制人的攻击,我不希望任何人回应,“因为devise者需要把它放在自动生成的代码中”。 这是可怕的。 我宁愿自动生成的代码不编译,直到你提供一个实现。 例如,自动生成的实现可能是“throw NotImplementedException;” NotImplementedException未定义!
有没有人抓到和处理NotImplementedException? 你有没有在你的代码中留下NotImplementedException? 如果是这样,这是否代表定时炸弹(即,你不小心把它留在那里),还是一个devise缺陷(该方法不应该实施,将永远不会被称为)?
我非常怀疑NotSupportedException也…不支持? 什么? 如果不支持,为什么它是你的界面的一部分? 微软的任何人都可以inheritance不正当的inheritance权吗? 但是,如果我不为这个滥用,我可能会开始另一个问题。
附加信息:
这是一个有趣的主题阅读。
与Brad Abrams似乎有一个很强的一致意见:“NotImplementedException是针对那些尚未实现的function,但是确实应该(而且将会是),就像你在构build一个类的时候开始的那样,获取所有的方法那里抛出NotImplementedException,然后用真正的代码冲出来……“
Jared Parsons的评论非常微弱,应该被忽略:NotImplementedException:当一个types由于其他原因没有实现一个方法的时候抛出这个exception。
MSDN在这个问题上甚至更弱一些,只是指出:“当一个请求的方法或操作没有被执行时抛出exception。
有一种情况我觉得很有用:TDD。
我写我的testing,然后创build存根,所以testing编译。 那些存根除了throw new NotImplementedException();
。 这样,无论如何,testing都会默认失败。 如果我使用了一些虚拟的返回值,可能会产生误报。 现在所有的testing编译失败,因为没有实现,我解决这些存根。
由于在任何其他情况下我从不使用NotImplementedException
,所以不会传递给释放代码,因为它总会使一些testing失败。
你不需要把它赶到所有的地方。 好的APIlogging抛出的exception。 那些是你应该寻找的。
编辑:我写了一个FxCop规则来find它们。
这是代码:
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>
要build立这个你需要:
-
参考
Microsoft.FxCop.Sdk.dll
和Microsoft.Cci.dll
-
将元数据放在名为
DesignRules.xml
的文件中,并将其作为embedded资源添加到您的程序DesignRules.xml
-
将您的程序集
Bevonn.CodeAnalysis
。 如果要为元数据或程序集文件使用不同的名称,请确保相应地将第二个参数更改为基础构造函数。
然后,只需将生成的程序集添加到FxCop规则中,并将那些该死的exception从宝贵的代码中拿出来。 有一些angular落的情况下,不会报告一个NotImplementedExceptionexception,但我真的认为你是绝望的,如果你真的写这样的cthulhian代码。 对于正常使用,即throw new NotImplementedException();
,它的工作,这是重要的。
它支持一个相当常见的用例,一个工作但只部分完成的API。 假设我想让开发人员testing和评估我的API – 至less在我的机器上使用了DryDishes()
,但是我还没有编写DryDishes()
,更不用说PutAwayDishes()
。 DryDishes()
不工作 – 我还没有实现它,而不是默默无闻,或给出一些神秘的错误信息,我可以很清楚。
它的姊妹exceptionNotSupportedException
对于提供者模型来说很有意义。 许多洗碗机都有干燥function,所以属于界面,但是我的折扣洗碗机不支持它。 我可以让它通过NotSupportedException
来知道
我将在一个地方总结我的看法,因为它们分散在一些评论中:
-
您使用
NotImplementedException
来指示接口成员尚未实现,但将是。 您可以将其与自动unit testing或QAtesting结合起来,以确定仍需要实施的function。 -
一旦实现该function,您将删除
NotImplementedException
。 新的unit testing是为了确保function正常工作而编写的。 -
NotSupportedException
通常用于不支持对特定types无意义的function的提供程序。 在这些情况下,具体的types抛出exception,客户端抓住他们,并酌情处理。 -
在框架中存在
NotImplementedException
和NotSupportedException
的原因很简单:导致它们的情况很常见,因此在框架中定义它们是有意义的,这样开发人员就不必重新定义它们。 此外,它使客户很容易知道要捕捉哪个exception(特别是在unit testing的情况下)。 如果你必须定义自己的例外,他们必须弄清楚哪个例外,这至less是一个反作用的时间汇,而且往往是不正确的。
为什么NotImplementedException存在?
NotImplementedException是一个很好的方式来说一些东西还没有准备好。 为什么它没有准备好是方法作者的一个单独的问题。 在生产代码中,你不太可能发现这个exception,但是如果你确实能够立刻看到发生了什么事情,那么比试图找出为什么调用方法但什么也没有发生甚至更糟 – 要获得一些“临时”的结果要好得多“有趣”的副作用。
是否NotImplementedException C#等价于Java的UnsupportedOperationException?
不,.NET有NotSupportedException
我必须运行我的algorithm,赶上NotImplementedExceptions和一些如何回滚我的应用程序到一个理智的状态
良好的API具有描述可能的exception的XML方法文档。
我非常怀疑NotSupportedException也…不支持? 什么? 如果不支持,为什么它是你的界面的一部分?
可能有数百万个原因。 例如,您可以引入新版本的API,不想/不能支持旧的方法。 同样,看到描述性的exception,而不是深入到文档或debugging第三方代码,会更好。
NotImplementedExceptionexception的主要用途是生成的存根代码:这样你就不会忘记实现它! 例如,Visual Studio将显式实现一个接口的方法/属性,并抛出一个NotImplementedExceptionexception。
重新NotImplementedException
– 这有几个用途; 它提供了一个例外,例如你的unit testing可以locking不完整的工作。 而且,它确实是这样做的:这根本就不存在(还)。 例如,“mono”会将这个方法引发到MS libs中存在但尚未写入的方法。
Re NotSupportedException
– 并非所有东西都可用。 例如,许多接口支持一对“你能做到这一点?” / “做这个”。 如果“你能做到这一点?” 返回false,对于“做这个”抛出NotSupportedException
是完全合理的。 例子可能是IBindingList.SupportsSearching
/ IBindingList.Find()
等
Microsoft的大多数开发人员都熟悉适用NotImplementedException的devise模式。 实际上这是相当普遍的。
一个很好的例子就是Composite Pattern ,其中许多对象可以被当作对象的单个实例。 一个组件被用作(正确)inheritance的叶类的基本抽象类。 例如,一个File和Directory类可能从同一个抽象基类inheritance,因为它们是非常相似的types。 这样,他们可以被视为一个单一的对象(当你想到什么文件和目录是有道理的 – 在Unix中,例如,一切都是文件)。
所以在这个例子中,Directory类有一个GetFiles()方法,但是File类不会实现这个方法,因为这样做没有意义。 相反,你会得到一个NotImplementedException,因为一个文件没有子目录的方式。
请注意,这不仅限于.NET – 您将在许多OO语言和平台中遇到这种模式。
为什么你觉得有必要抓住每一个可能的例外? 你是否用catch (NullReferenceException ex)
封装了每个方法调用呢?
存根代码抛出NotImplementedException
是一个占位符,如果它释放它应该像NullReferenceException
一样的错误。
我认为MS为框架添加NotImplementedException有很多原因:
- 为了方便; 因为很多开发者在开发过程中都需要它,为什么每个人都要自己推出呢?
- 所以工具可以依靠它的存在; 例如,Visual Studio的“实现接口”命令生成引发NotImplementedException的方法存根。 如果它不在框架中,这是不可能的,或者至less相当尴尬(例如,它可能会生成代码,直到添加自己的NotImplementedException时才会编译)
- 为了鼓励一致的“标准做法”
Frankodwyer认为NotImplementedException是一个潜在的时间炸弹。 我会说,任何未完成的代码是一个时间炸弹,但NotImplementedException比替代scheme更容易撤防。 例如,您可以让您的构build服务器扫描该类的所有用途的源代码,并将其报告为警告。 如果你想真的禁止它,你甚至可以添加一个预先提交的钩子到你的源代码pipe理系统,防止检入这样的代码。
当然,如果你滚动你自己的NotImplementedException,你可以从最终的版本中删除它,以确保没有时间炸弹。 但是,如果您在整个团队中始终如一地使用您自己的实施,那么这只会起作用,您必须确保在发布前不要忘记将其删除。 另外,你可能会发现你不能删除它; 也许有一些可以接受的用途,例如在testing没有发货给客户的代码中。
实际上没有任何理由要捕获 NotImplementedException。 点击时,它应该杀死你的应用程序,并非常痛苦。 解决这个问题的唯一方法不是通过捕获它,而是更改源代码(实现被调用的方法或更改调用代码)。
两个原因:
-
在开发过程中,方法被剔除,抛出exception,提醒开发者他们的代码写入没有完成。
-
实现一个子类接口,在devise上,它不会实现inheritance的基类或接口的一个或多个方法。 (有些接口太笼统了。)
这听起来像是我的一个潜在的雷区。 在遥远的过去,我曾经工作过的一个传统的networking系统,这个networking系统已经运行好几年了,而且这个系统已经有一天的时间了。 当我们追查这个问题的时候,我们发现一些代码显然还没有完成,而且这些代码根本不可能工作 – 就像程序员在编写代码时被打断一样。 很明显,这个特定的代码path从来没有被采用过。
墨菲定律说类似的东西只是在NotImplementedException的情况下才会发生。 在TDD等这些日子里,应该在发布之前拿起来,至less你可以在发布之前grep代码,但仍然可以。
在testing时,很难保证每个案例的覆盖范围,而这听起来像是通过使运行时间问题成为可能是编译时间问题,使得您的工作变得更加困难。 (我认为类似的“技术债务”来与严重依赖“鸭子打字”的系统,而我承认他们是非常有用的)。
COM互操作需要此exception。 这是E_NOTIMPL 。 链接的博客还显示其他原因
抛出NotImplementedException
是IDE生成编译存根代码的最合理的方法。 就像当你扩展接口并让Visual Studio为你存根一样。
如果你做了一点C ++ / COM,那也是存在的,除了它被称为E_NOTIMPL
。
有一个有效的用例。 如果您正在使用某个接口的特定方法,则需要编译代码,以便进行debugging和testing。 根据你的逻辑,你需要从接口中删除方法,并注释掉非编译的存根代码。 这是一种非常原教旨主义的做法,虽然有其优点,但不是每个人都会坚持这一点。 此外,大部分时间你想要接口是完整的。
有一个NotImplementedException很好地标识哪些方法还没有准备好,在一天结束的时候,就像按Ctrl+Shift+F
来find它们一样简单,我也确信静态代码分析工具也会把它拿起来。
您不是要发送有NotImplementedException
exception的代码。 如果你认为不使用它可以使你的代码更好,那么可以做更多有效的事情来提高源代码质量。
NotImplementedException
当请求的方法或操作未执行时抛出exception。
使这个在.NET内核中定义的单个exception更容易find并消除它们。 如果每个开发人员都应该创build自己的ACME.EmaNymton.NotImplementedException
,那么将很难find所有这些。
NotSupportedExceptionexception
当不支持被调用的方法时抛出exception。
例如,当试图读取,寻找或写入不支持被调用的function的stream时。
例如生成的迭代器(使用yield
关键字)是一个IEnumerator
,但IEnumerator.Reset
方法抛出NotSupportedException
。
从ECMA-335,CLI规范,特定于CLI库types,System.NotImplementedException,备注部分:
“本标准中其他地方指定的许多types和结构对于仅符合内核configuration文件的CLI实现是不需要的,例如,浮点特性集由浮点数据typesSystem.Single和System.Double。如果在实现中省略了这些支持,则任何尝试引用包含浮点数据types的签名都将导致System.NotImplementedExceptiontypes的exception。
所以,这个例外是针对只实现最小一致性configuration文件的实现。 最低要求的configuration文件是内核configuration文件(参见ECMA-335第四版 – 分区IV,第3节),其中包括BCL,这就是为什么exception包含在“核心API”中,而不是在其他位置。
使用exception来表示存根方法,或者对于缺乏实现的devise者生成的方法来说,是误解了exception的意图。
至于为什么这个信息不包含在MS的文档中,MS的CLI的实现超出了我的范围。
我不能担保NotImplementedException(我主要同意你的看法),但我在我们使用的核心库中广泛使用了NotSupportedException。 例如,DatabaseController允许您创build任何支持types的数据库,然后在整个剩余代码中使用DatabaseController类,而不必关心底层数据库的types。 相当基本的东西,对吧? 如果NotSupportedException派上用场(如果不存在的话,我会用自己的实现)是两个主要的实例:
1)将应用程序迁移到不同的数据库通常很less有人争辩说,如果有的话,或者需要。 瞎扯淡。 多出去
2)相同的数据库,不同的驱动程序最新的例子是,当一个客户端使用由WinXP升级到Win7 x64的Access支持的应用程序。 没有64位JET驱动程序,他们的IT人员安装了AccessDatabaseEngine。 当我们的应用程序崩溃时,我们可以很容易地从日志中看到它是DB.Connect崩溃与NotSupportedException – 我们很快能够解决。 另一个最近的例子是我们的一个程序员试图在Access数据库上使用事务。 即使Access支持事务,但我们的库不支持Access事务(由于本文范围之外的原因)。 NotSupportedException,这是你的时间闪耀!
3)generics函数在这里我想不出一个简洁的“从经验”的例子,但是如果你想像一个函数,添加一个附件到电子邮件,你希望它能够采取一些常见的文件,如JPEG,任何派生自各种stream类的东西,以及任何具有“.ToString”方法的东西。 对于后者,你当然不能解释每种可能的types,所以你把它变成通用的。 当用户通过OurCrazyDataTypeForContainingProprietarySpreadsheetData时,使用reflection来testing是否存在ToString方法,并返回NotSupportedException来指示不支持不支持ToString的数据types。
NotSupportedException并不是一个重要的function,但是当我处理大型项目时,我发现自己使用了更多的东西。
比方说你有你的生产代码中的这种方法
public void DoSomething()
如果你想稍后离开,你会select哪一个?
public void DoSomething() { }
要么
public void DoSomething() { throw new NotImplementedException(); }
我肯定会拿第二名。 再加上Elmah或其他任何错误logging机制(作为整个应用程序的一个方面来实现)。 与日志/exception过滤一起触发一个被发现的关键错误电子邮件通知。
The argument that NotImplementedException == unfinished isn't correct either. (1) Catching of unimplemented methods should be left to unit tests/ integration tests. If you have 100% coverage (which you should now do, with so many many mock/stub/code generation tools) with no NotImplementedException, what are your worries? (2) Code generation. Plain simple. Again, if I generate the code, and only use half of the generated code, why wouldn't I have NotImplementedException in the rest of the generated stub?
It's like saying code shouldn't compile unless every nullable input should be checked/handled for null. (AKA the trillion dollar mistake, if not more). Language should be flexible, while tests/contracts should be solid.
Rarely I do use it for interface fixing. Assume that you've an interface that you need to comply but certain method will be never called by anyone, so just stick a NotImplementedException and if someone calls it they will know they are doing something wrong.
NotImplementedException is thrown for some method of .NET (see the parser C# in Code DOM which is not implemented, but the method exist !) You can verify with this method Microsoft.CSharp.CSharpCodeProvider.Parse
They are both hacks for two common problems.
NotImplementedException is a workaround for developers who are architecture astronauts and like to write down the API first, code later. Obviously, since this is not a incremental process, you can't implement all at once and therefore you want to pretend you are semi-done by throwing NotImplementedException.
NotSupportedException is a hack around the limitation of the type systems like those found in C# and Java. In these type systems, you say that a Rectangle 'is a' Shape iff Rectangle inherits all of Shapes characteristics (incl. member functions + variables). However, in practice, this is not true. For example, a Square is a Rectangle, but a Square is a restriction of a Rectangle, not a generalization.
So when you want to inherit and restrict the behavior of the parent class, you throw NotSupported on methods which do not make sense for the restriction.
What about prototypes or unfinished projects?
I don't think this is a really bad idea to use an exception (although I use a messagebox in that case).
Well, I somewhat agree. If an interface has been made in such a way that not all class can implement all bits of it, it should've been broken down in my opinion.
If IList can or cannot be modified, it should've been broken down into two, one for the unmodifiable part (getters, lookup, etc.), and one for the modifiable part (setters, add, remove, etc.).
I have a few NotImplementedExceptions in my code. Often times it comes from part of an interface or abstract class. Some methods I feel I may need in the future, they make sense as being part of the class, but I just don't want to take the time to add unless I actually need it. For example, I have an interface for all the individual kinds of stats in my game. One of those kinds are a ModStat, which is the sum of the base stat plus all the modifiers (ie weapons, armor, spells). My stat interface has an OnChanged event, but my ModStat works by calculating the sum of all stats it references each time it is called. So instead of having the overhead of a ton of ModStat.OnChange events being raised every time a stat changes, I just have a NotImplementedException thrown if anyone tries to add/remove a listener to OnChange.
.NET languages are all about productivity, so why spend your time coding something you won't even use?
Here is one example: In Java, whenever you implement the interface Iterator
, you have to override the obvious methods hasNext()
and next()
, but there is also delete()
. In 99% of the usecases I have I do not need this, so I just throw a NotImplementedException
. This is much better than silently doing nothing.
The NotImplementedException exists only to facilitate development. Imagine you start implementing an interface. You'd like to be able to at least build when you are done implementing one method, before moving to the next. Stubbing the NotImplemented methods with NotImplementedExceptions is a great way of living unfinished code that is super easy to spot later. Otherwise you would run the risk of quickly implementing something that you might forget to fix.
If you don't want to use it then just ignore it. If you have a block of code whose success depends on every piece of it succeeding, but it might fail in between, then your only option is to catch the base Exception
and roll back what needs to be rolled back. Forget NotImplementedException
. There could be tons of exceptions thrown, like MyRandomException
and GtfoException
and OmgLolException
. After you originally write the code I could come by and throw ANOTHER exception type from the API you're calling. One that didn't exist when you wrote your code. Handle the ones you know how to handle and rollback for any others ie, catch(Exception)
. It's pretty simple, I think… I find it comes in handy too. Especially when you're trying fancy things with the language/framework that occasionally force things upon you.
One example I have is serialization. I have added properties in my .NET library that don't exist in the database (for example, convenient wrappers over existing "dumb" properties, like a FullName
property that combines FirstName
, MiddleName
, and LastName
). Then I want to serialize these data types to XML to send them over the wire (for example, from an ASP.NET application to JavaScript), but the serialization framework only serializes public properties with both get
and set
accessors. I don't want you to be able to set FullName
because then I'd have to parse it out and there might be some unforeseen format that I wrongly parse and data integrity goes out the window. It's easier to just use the underlying properties for that, but since the language and API require me to have a set
accessor, I'll throw a NotImplementedException
(I wasn't aware of NotSupportedException
until I read this thread, but either one works) so that if some programmer down the road does try to set FullName
he'll encounter the exception during testing and realize his mistake.