共用方式為


Exception instead of NotImplementedException

A number of people have expressed confusion and concern as to why C# code generation will insert an Exception with the text ‘the method or operation is not implemented,’ instead of simply generating a NotImplementedException. For example, if you create a simple class which implements IComparable and then use the implement interface smart tag: 

The code which is generated is:

  public int CompareTo(Widget other)
    {
        throw new Exception("The method or operation is not implemented.");
    }

Clearly NotImplementedException makes more sense here and, in fact, in early CTPs that was exactly what was generated. We ultimately had to change the code generation, however, because we have a tenet that generated code should always build (at least, when the code was in a buildable state prior to the generation). However, Visual Studio 2005 supports targeting the 1.0 version of the compact framework. In order to keep the size of the compact framework small, it does not include all of the same types that exist in the desktop framework. One of the types that is not included is NotImplementedException. 

We decided to change this somewhat late, so we didn’t try to put in logic to change the code generation only in the case that the target file was in a project which was targeting the 1.0 version of the compact framework… but that seems like a reasonable thing to do in the future.

There are plenty of developers who never target the compact framework version 1.0 though, so it would be really convenient if there was a way to change the generated code back to NotImplementedException. It turns out there is. In the directory Program Files\Microsoft Visual Studio 8\VC#\Snippets\1033\Refactoring\ there are code snippets which the C# language service uses whenever it does code generation. To change the way that CompareTo is generated via implement interface, MethodStubBody.snippet can be changed.

For example, MethodStubBody.snippet appears as:

<?

xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="https://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>Method Stub - Body</Title>
<Description>Snippet for generating method stub with a body</Description>
<Author>Microsoft Corporation</Author>
<SnippetTypes>
<SnippetType>Refactoring</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal Editable="true">
<ID>signature</ID>
<Default>signature</Default>
</Literal>
<Literal>
<ID>Exception</ID>
<Function>SimpleTypeName(global::System.NotImplementedException)</Function>
</Literal>
</Declarations>
<Code Language="csharp">
<![CDATA[$signature$
{
$end$throw new $Exception$();
}]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>

There is a good introduction to the Code Snippet schema here; but in this case the most interesting bit is the <Code> section. Notice that it looks very similar to the code that is being generated for CompareTo. $signature$ is a special literal that is replaced by the language service with the signature of the method being implemented, when implement interface is invoked. $Exception$ is a replaceable field (or literal) defined in the declarations section. In this case, the $Exception$ literal is defined as a function. Functions are unique to C# Code Snippets, but currently we provide only a small number. In this case the function being called is SimpleTypeName, which basically reduces the type passed in to the simplest type name in the context into which it is generated. That is, if there is no using directive for the System namespace, then when this snippet is inserted $Exception$ will be replaced with System.Exception. If there is a using directive for System, then $Exception$ will be replaced with Exception. 

This snippet can be modified to change the code generated. In this case, simply delete the string passed to the exception and change the type passed in to SimpleTypeName to be global::System.NotImplementedException. The result appears as:

  <Declarations>
<Literal>
<ID>signature</ID>
<Default>signature</Default>
</Literal>
<Literal>
<ID>NotImplementedException</ID>
<Function>SimpleTypeName(global::System.NotImplementedException)</Function>
</Literal>
</Declarations>
<Code Language="CSharp">
<![CDATA[$signature$
{
throw new $NotImplementedException$();
}]]>
</Code>

Now save it and try implement interface again:

     public int CompareTo(Widget other)
    {
        throw new NotImplementedException();
    }

The snippets in the refactoring subdirectory are used with different features. For example, MethodOverrideStub.snippet is used when “override” is typed in the editor and a method is generated based on the completion list that appears. However, some features currently share snippets. For example, both implement interface and generate method stub use MethodStub.snippet, so changing it will change the generation of both of those features.

Comments

  • Anonymous
    December 13, 2005
    Thanks for this!

    BTW - you need to change the ID element value in your example to...

    <ID>NotImplementedException</ID>
  • Anonymous
    December 13, 2005
    Thanks Sean! I've updated the post.
  • Anonymous
    February 26, 2006
    PingBack from http://daily.feature42.com/PermaLink,guid,364306bd-2146-4bbf-9d68-20b3e0add1bf.aspx
  • Anonymous
    August 01, 2006
    PingBack from http://www.robherbst.com/blog/2006/07/30/the-notimplementedexception/
  • Anonymous
    October 25, 2006
    Ever since I started using Visual Studio 2005, I've been enjoying its refactoring features, but it has
  • Anonymous
    December 07, 2006
    Why do you need to change Literal/ID value to NotImplementedException if Literal/ID/Function value was already SimpleTypeName global::System.NotImplementedException)?
  • Anonymous
    March 23, 2007
    In Andre 's recent post he asked why Visual Studio generates method stubs that throw a System.Exception