Implement interface in Whidbey


Shaykat, another PM on the C# team, recently posted (http://blogs.msdn.com/shaykatc/archive/2004/03/10/87582.aspx) a VS 2003 tip for automatically implementing an interface.  We have received almost universally positive feedback about it, but there were definitely some gotchas:



  • It works quite well for implicitly implementing an interface for the first time, but it’s not reentrant with an easy interaction (you almost always have to delete and re-type the interface name), such that if you add additional members to an interface it’s hard to use the feature to implement stubs for those.
  • In 2003 we attempted to intelligently choose whether to implement the interface implicitly or explicitly.  If there were existing members with the same name but a different signature then we would implement that member explicitly.  Unfortunately we weren’t entirely consistent so you’d occasionally end up with code that wouldn’t build.  That type of implicit vs. explicit inference also turned out to be a bug farm.
  • The code spit that we chose for generating the stubs is sub-optimal.  It would determine the return type and return either null if it was a reference type, or a default value for a value type (if it was a known value type like int, then it would return 0; if it was unknown then it would return “new ValueTypeName();“).  Unfortunately this means that you can write code against these interface members and have the code ‘work’, even though there is no backing implementation.  This is occasionally useful, but we’ve had lots of requests to throw a NotImplementedException instead.
  • There was no way to modify the code spit.  You couldn’t add or remove TODO comments, for example.

We have attempted to tackle all of these issues in Whidbey.  In order to invoke this feature in Whidbey you’ll use an editor ‘smart tag’.  This tag is a little marker that appears underneath an interface name when the cursor is on the same line in which it appears.  It can be invoked through the keyboard or by hovering the mouse over the marker.  After invocation a context like menu will appear, which in this case, will offer two options.  To either implement the interface implicitly, or explicitly. 


Shows the highlighted marker under IComparable
Shows the expanded menu giving a choice between implicit and explicit implementations


You’ve probably already determined how this addressees the first two issues.  First, the smart tag is always available, so if you add additional members to an interface then it’s easy to invoke the feature again and generate the additional stubs.  Second, we no longer attempt to determine whether to implement the interface explicitly or implicitly, instead we leave that decision in the hands of the developer (where it belongs). 


The code generation bullets are interesting, because there are a lot of features in Whidbey that generate code in some manner.  Given that, we decided to leverage our expansions feature (I’ll blog about this separately if you haven’t read about it elsewhere yet) to allow developers to modify the generated code.  For example, the implement interface feature uses an expansion that looks like this:



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


The $signature$ literal is replaced when the code is generated.  To put a comment in every generated method stub, you would just add it to this file.  The $NotImplementedException$ literal is worth mentioning as well.  Its replacement value is determined by a function.  In this case, the function “SimpleTypeName” is used, which determines what the ‘simplest’ form of global::System.NotImplementedException is.  That is, if you were to implement an interface in a context with no using directives, it would insert System.NotImplementedException; however, if you were to implement the same interface in a scope in which the System namespace had been imported then it would use ‘throw NotImplementedException()’.


Comments (23)

  1. Scott Munro says:

    Sounds great. I blogged on my thoughts for how we should be passing arguments to refactorings and the ways that we will be initiating them.

    Do you have any thoughts on Intelliarguments and Refactoring Initiations?

    http://dotnetjunkies.com/WebLog/scottmunro/archive/2004/03/14/9066.aspx

  2. The most annoying problem I have with the "implement interface" feature is that I have to copy the documentation comments manually. I blogged about this here:

    http://weblogs.asp.net/rweigelt/archive/2004/03/10/87449.aspx

  3. I’m assuming there’s a key combniation for either one so that I don’t have to use my mouse…

  4. Vrye Bredo says:

    While thinking about these refactoring etc, I have to ask, when can iSpeak theCode and refactor etc pointing my finger or pen at touch/drawing screen? Graphic artists have long used screens you can draw on, i’m just waiting for the interface where you can transform the ‘inaudible speech’ to code without having to speak it out loud.

  5. Andrew Deren says:

    would be nice to have option not to generate #regions. I hate it when it generates the interface and then I use arrow keys to scroll down and the region collapses.

    Would also be nice to be able to regenerate new methods (maybe it’s in whidbey already?)

    When I add method declarations to interfaces then I could easly add those to my classes.

    If you’re looking for a really good example of refactoring you should look at eclipse (I bet you get that alot)

    One really nice feature is when you compile you get little icons in the gutter with common fixes. For example if a class is missing a method from interface (or abstract class) you can choose to add this method (or make the class abstract).

    There are many other things they do good, that would be nice to have in VS.

  6. Red Forks says:

    Looks good!

    But I hate use mouse when keyin code.

    How about simply popup a menu, then use up/down, enter/esc keys?

    Popup menu easily support both mouse & keyboard.

    SmartTag is useful in mouse-orianted enviroments, such as excel or web browser.

  7. Red Forks says:

    Looks good!

    But I hate use mouse when keyin code.

    How about simply popup a menu, then use up/down, enter/esc keys?

    Popup menu easily support both mouse & keyboard.

    SmartTag is useful in mouse-orianted enviroments, such as excel or web browser.

  8. CorneliuT says:

    Hi,

    How about 2 new autocomplete types:

    1. Auto complete Enums

    2. Auto complete declarations;

    I see both quite big coding performance enhancements that should be part of the IntelliSense.

    However most of the times your project is getting big and have enumerations declared in lots of namespaces that are not necessary all imported.

    Thus, I would like to be able to just write:

    this.Dock =

    and in this momment the Intellisense should help me by writing the partial/full namespace+enumeration name for me like this:

    this.Dock = System.Windows.Forms.DockStyle

    so I just press the "." and write select the value I need.

    The autocomplete could be triggerd like the one for delegates using the TAB key after you write the "+=".

    I think is would extremly nice and helpfull to have especially when working on large projects where you can’t remember all the enumerations from all the namespaces.

    2. Auto complete declarations;

    For the declarations IntelliSense should work the same by providing you the option to press tab and complete the name of the class you need to declare in that point:

    eg: I have a collection: myapp.ns1.ns2.ns3.MyObjectsCollection and an instance myobjects.

    To do an add I have to write:

    myObjects.Add ( new myapp.ns1.ns2.ns3.MyObject () );

    I would like to write only:

    myObjects.Add (

    press TAB and get:

    myObjects.Add ( new myapp.ns1.ns2.ns3.MyObject (

    This would be very nice also.

    It could also work on the righthand side of an operations eg:

    instead of writing:

    myapp.ns1.ns2.ns3.MyObject newObj = myObjects[ 0 ];

    I would like to write

    myObjects[ 0 ];

    move the cursor at the begining, insert an "=" and press TAB or something similar and get:

    myapp.ns1.ns2.ns3.MyObject = myObjects[ 0 ];

    This would be trully nice 🙂

    Anybody else cares about this two functionalities ?

    Thanks,

    Corneliu.

  9. Staffan Gustafsson says:

    In general, I really hope that everyone on the team working with completion has toyed at least a little bit with JetBrains IntelliJ. They really do set the standard for smartness in an editor. If you get close to that, you’ve done a great job!

    If you surpass them? WOW!

    /S

  10. bokka says:

    Thank you fpr you.I want copy this site.

  11. Update (1/31/08): It looks like Snippy has now found a home on CodePlex . I haven’t been maintaining