Why does NotImplementedException exist?

2019-01-04 17:11发布

This really, really urks me, so I hope that someone can give me a reasonable justification for why things are as they are.

NotImplementedException. You are pulling my leg, right?

No, I'm not going to take the cheap stab at this by saying, "hang on, the method is implemented - it throws a NotImplementedException." Yes, that's right, you have to implement the method to throw a NotImplementedException (unlike a pure virtual function call in C++ - now that makes sense!). While that's pretty damn funny, there is a more serious problem in my mind.

I just wonder, in the presence of the NotImplementedException, how can anyone do anything with .Net? Are you expected to wrap every abstract method call with a try catch block to guard against methods that might not be implemented? If you catch such an exception, what the heck are you supposed to do with it??

I see no way to test if a method is actually implemented without calling it. Since calling it may have side effects, I can't do all my checks up-front and then run my algorithm. I have to run my algorithm, catch NotImplementedExceptions and the some how roll back my application to some sane state.

It's crazy. Mad. Insane. So the question is: Why does the NotImplementedException exist?

As a preemptive strike, I do not want anyone to respond with, "because designers need to put this in the auto-generated code." This is horrid. I would rather the auto-generated code not compile until you supply an implementation. For example, the auto generated implementation could be "throw NotImplementedException;" where the NotImplementedException is not defined!

Has anyone ever caught and handled a NotImplementedException? Have you ever left a NotImplementedException in your code? If so, did this represent a time bomb (ie, you accidentally left it there), or a design flaw (the method should not be implemented and will never be called)?

I'm very suspicious of the NotSupportedException also... Not supported? What the? If it's not supported, why is it part of your interface? Can anyone at Microsoft spell improper inheritance? But I might start another question for that if I don't get too abuse for this one.

Additional info:

This is an interesting read on the subject.

There seems to be a strong agreement with Brad Abrams that "NotImplementedException is for functionality that is just not yet implemented, but really should (and will be). Something like what you might start with when you are building a class, get all the methods there throwing NotImplementedException, then flush them out with real code…"

Comments from Jared Parsons are very weak and should probably be ignored: NotImplementedException: Throw this exception when a type does not implement a method for any other reason.

The MSDN is even weaker on the subject, merely stating that, "The exception that is thrown when a requested method or operation is not implemented."

27条回答
Deceive 欺骗
2楼-- · 2019-01-04 17:42

There is really no reason to actually catch a NotImplementedException. When hit, it should kill your app, and do so very painfully. The only way to fix it is not by catching it, but changing your source code (either implementing the called method, or changing the calling code).

查看更多
太酷不给撩
3楼-- · 2019-01-04 17:42

NotImplementedException

The exception is thrown when a requested method or operation is not implemented.

Making this a single exception defined in the .NET core makes it easier to find and eradicate them. If every developer should create their own ACME.EmaNymton.NotImplementedException it would be harder to find all of them.

NotSupportedException

The exception is thrown when an invoked method is not supported.

For instance when there is an attempt to read, seek, or write to a stream that does not support the invoked functionality.

For instance generated iterators (using yield keyword) is-a IEnumerator, but the IEnumerator.Reset method throws NotSupportedException.

查看更多
4楼-- · 2019-01-04 17:42

I can't vouch for NotImplementedException (I mostly agree with your view) but I've used NotSupportedException extensively in the core library we use at work. The DatabaseController, for example, allows you to create a database of any supported type then use the DatabaseController class throughout the rest of your code without caring too much about the type of database underneath. Fairly basic stuff, right? Where NotSupportedException comes in handy (and where I would have used my own implementation if one didn't already exist) is two main instances:

1) Migrating an application to a different database It's often argued this rarely, if ever, happens or is needed. Bullsh*t. Get out more.

2) Same database, different driver Most recent example of this was when a client who uses an Access-backed application upgraded from WinXP to Win7 x64. Being no 64-bit JET driver, their IT guy installed the AccessDatabaseEngine instead. When our app crashed, we could easily see from the log it was DB.Connect crashing with NotSupportedException - which we were quickly able to address. Another recent example was one of our programmers trying to use transactions on an Access database. Even though Access supports transactions, our library doesn't support Access transactions (for reasons outside the scope of this article). NotSupportedException, it's your time to shine!

3) Generic functions I can't think of a concise "from experience" example here but if you think about something like a function that adds an attachment to an email, you want it to be able to take a few common files like JPEGs, anything derived from various stream classes, and virtually anything which has a ".ToString" method. For the latter part, you certainly can't account for every possible type so you make it generic. When a user passes OurCrazyDataTypeForContainingProprietarySpreadsheetData, use reflection to test for the presence of a ToString method and return NotSupportedException to indicate lack of support for said data types that don't support ToString.

NotSupportedException isn't by any means a crucial feature but it's something I find myself using a lot more as I work on larger projects.

查看更多
兄弟一词,经得起流年.
5楼-- · 2019-01-04 17:43

There is one situation I find it useful: TDD.

I write my tests, then I create stubs so the tests compile. Those stubs do nothing but throw new NotImplementedException();. This way the tests will fail by default, no matter what. If I used some dummy return value, it might generate false positives. Now that all tests compile and fail because there is no implementation, I tackle those stubs.

Since I never use a NotImplementedException in any other situation, no NotImplementedException will ever pass onto release code, since it will always make some test fail.

You don't need to catch it all over the place. Good APIs document the exceptions thrown. Those are the ones you should look for.

EDIT: I wrote an FxCop rule to find them.

This is the code:

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);
        }
    }
}

And this is the rule metadata:

<?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>

To build this you need to:

  • reference Microsoft.FxCop.Sdk.dll and Microsoft.Cci.dll

  • Put the metadata in a file called DesignRules.xml and add it as an embedded resource to your assembly

  • Name your assembly Bevonn.CodeAnalysis. If you want to use different names for either the metadata or the assembly files, make sure you change the second parameter to the base constructor accordingly.

Then simply add the resulting assembly to your FxCop rules and take those damned exceptions out of your precious code. There are some corner cases where it won't report a NotImplementedException when one is thrown but I really think you are hopeless if you're actually writing such cthulhian code. For normal uses, i.e. throw new NotImplementedException();, it works, and that is all that matters.

查看更多
贪生不怕死
6楼-- · 2019-01-04 17:44

Throwing NotImplementedException is the most logical way for the IDE to to generate compiling stub code. Like when you extend the interface and get Visual Studio to stub it for you.

If you did a bit of C++/COM, that existed there as well, except it was known as E_NOTIMPL.

There is a valid use case for it. If you are working on a particular method of an interface you want you code to compile so you can debug and test it. According to your logic you would need to remove the method off the interface and comment out non-compiling stub code. This is a very fundamentalist approach and whilst it has merit, not everyone will or should adhere to that. Besides, most of the time you want the interface to be complete.

Having a NotImplementedException nicely identifies which methods are not ready yet, at the end of the day it's as easy as pressing Ctrl+Shift+F to find them all, I am also sure that static code analysis tools will pick it up too.

You are not meant to ship code that has NotImplementedException exception. If you think that by not using it you can make your code better, go forth, but there are more productive things you can do to improve the source quality.

查看更多
爱情/是我丢掉的垃圾
7楼-- · 2019-01-04 17:44

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.

查看更多
登录 后发表回答