I haven’t had a chance to post much recently, but I ran into a nice mail on an internal alias that listed the restrictions of set next statement. Set next statement is a very powerful feature of the debugger that allows the IP (instruction pointer) to be moved. It’s particularly useful when combined with Edit and Continue. This feature can be accessed from the context menu of the editor when the debugger is active. It may also be accessed by simply dragging the yellow arrow that appears on the left hand side of the screen while debugging. There are a number of restrictions associated with changing the IP including:
-
It can only be set within the same function/method
-
There are restrictions around setting it into or out of a catch block (exception handler)
-
It must usually be moved to source lines
-
The IP cannot be set when the debugger is stopped at a first chance exception
-
The IP can only be set in the active frame (the frame that is actually executing). That is, you cannot go 6 frames up the call stack and try to change the return address.
The most important thing to understand about this feature is that there is no real magic going on. It does a very minimal amount of work to move the IP; it does not try to change values back to their previous state if the IP is moved higher up in a method. If you’re tracking down a bug in your application, it’s often useful to step over most methods until something unexpected happens (e.g. a return value isn’t what you expect). You can then use set next statement to re-run the method but this time step into it and examine what went wrong. This can help track down bugs much faster, and of course, with Edit and Continue you can often fix the problem and then re-run the offending block of code to see if the change worked.
Imagine that you have this simple, somewhat useless bit of code:
using System;
class Example
{
static void Main(string[] args)
{
int initialValue = 20;
initialValue++;
Console.WriteLine(initialValue);
}
}
Let’s suppose that you’re debugging this and your IP is located on Console.WriteLine:
Now you right-click on the source line containing initialValue++ and select “Set Next Statement”:
Finally you hit F5 to finish running the application. The value written to the console is 22, not 21. Again, this is because Set Next Statement doesn’t perform any kind of state analysis, it simply moves the IP to the location indicated. In this case, it’s possible to get the original behavior by simply moving the IP up to the source line that sets the value of initialValue. That’s trivial in this case since the initial assignment is immediately above the modification. If there is a significant amount of code that you don’t want to re-run, then you can also modify the value in the locals/watch/etc. windows instead.
Extern aliases is a feature that was introduced in VS 2005 that I don’t believe is being used very often. There are two reasons for this. First, the feature is somewhat undiscoverable, particularly if you don’t happen to be looking for it. Second, the feature itself is useful in the somewhat narrow scenario of needing to explicitly differentiate between two type names that are identical except for the assembly strong name (name, version, public key, etc.) in which they are defined.
Of course, if you happen to be in that situation this feature can prove invaluable.
I wrote the walkthrough below while we were developing the feature; it should provide a reasonable starting point for how extern aliases can be used in Visual C#.
- Create a C# Class Library called FooVersion1
- Replace the template code in Class1.cs with the following:
using System;
namespace Acme
{
public class Foo
{
public void Bar()
{
Console.WriteLine("Bar");
}
}
}
- Right-click on the solution in solution explorer and select Add | New Project
- Save the current project (only applicable in express)
- Select a Class Library in the new project dialog and change the project name to FooVersion2 and press OK
- Replace the code in Class1.cs with the following:
using System;
namespace Acme
{
public class Foo
{
public void Bar()
{
Console.WriteLine("Bar");
}
public void Goo()
{
Console.WriteLine("Goo");
}
}
}
- Right-click on the solution in solution explorer and select Add | New Project
- Select a Console Application and call it Consumer
- Right-click on Consumer and select ‘Set as startup project’
- Right-click on the references node in the Consumer project and select ‘Add Reference’
- Click on the projects tab, and multi-select FooVersion1 and FooVersion2
- Click OK
- Add the following line to Main in the Program type of the Consumer project:
Acme.Foo f = new Acme.Foo();
- Build the solution via Ctrl+Shift+B (or F6)
- Notice that you get two build errors:
- Open solution explorer and select FooVersion1 in the References folder of the Consumer project
- Hit F4 (or select View | Properties Window)
- Change the Aliases property to FooVersion1
- Build the solution
- Now everything will build correctly, because Acme.Foo unambiguously refers to FooVersion2
- Add the following directive to the top of Program.cs in the Consumer project:
extern alias FooVersion1;
- Change the usage of Acme.Foo to:
FooVersion1::Acme.Foo f = new FooVersion1::Acme.Foo();
f.Bar();
- Notice that when you type ‘f.’ the completion list contains only those methods in FooVersion1 of Acme.Foo (notably it does not include Goo)
- Build the solution and everything will build correctly
- Finally add the following code under f.Bar() in Program.cs of the Consumer project:
Acme.Foo f2 = new Acme.Foo();
f2.Goo();
- Notice that f2’s completion list contains Goo.
- Build again using Ctrl+Shift+B and notice that there are still no build errors
This shows a usage pattern which we expect to be relatively common when these ambiguities exist. The assembly which will be used less often will be aliased while the other will continue to exist in the global namespace (such that it doesn’t require qualification through an extern alias).
A recent post in the Visual C# IDE forum (seems like I start a lot of blogs this way J) got me thinking about function evaluation (“FuncEval”) while debugging. I don’t think there is any scarier term for a debugger developer – seriously, I’ve seen them cower in fear. FuncEval is, quite simply, the process of invoking a method while stopped in the debugger. This feature is invaluable for managed code debugging for a number of reasons; chief among them, however, is the very simple desire to see the value of properties in the various data windows. Keep in mind that underlying a property are the getter and setter methods. FuncEval not only enables property evaluation, but also features like debugger visualizers, design time expression evaluation (and expression evaluation in general), the object test bench and many others that make the managed world a fun place to be.
So, why would a debugger developer hide whimpering in a corner every time a new feature based on FuncEval is introduced? The Visual Studio debugger is designed to allow deep inspection of data at a given point of time in an application’s execution. That ‘point in time’ can be hit via a breakpoint, stepping, an exception being thrown, etc. Now, let’s suppose that the problem is an exception. The debugger may stop the program at the point where the exception is unhandled, enters user code, is thrown – generally good places to understand what the problem is. The point is at that moment in time the state of the program is such that an exceptional situation occurred and the debugger’s job is to give the developer enough information about the state to find out how to prevent it in the future. The problem is that function evaluations can modify that state. This is often referred to as a side effect of the evaluation though, in truth, it could easily be the desired behavior of the evaluation and only a side effect when evaluated in the debugger. An example will probably clarify this a bit:
Run this program under the debugger. An exception is thrown in BehavingBadly.DoSomething because handle is null. Let’s suppose that the first thing that the developer does is to open up the locals window and expand ‘this’ to examine the state of the object. At that point, the Handle property is executed, it sees that the field is set to null, and subsequently allocates a new Handle for it. This could clearly be confusing – a NullReferenceException, but handle is clearly assigned a value!
It turns out that it’s even more insidious. Function evaluations are order dependent. If an ‘implicit’ function evaluation, like a property, changes state that another field depends on then the value shown in the debugger may not actually reflect the current value of the field. For example:
Set a breakpoint on the closing brace of Main and start debugging. Examine p in the watch window. Notice that the debugger says that the current value of p.a is 0. Type p.a in the watch window. It now says the value of p.a is 1. This becomes even more interesting from a UI perspective because you may have multiple windows showing the same data (like the locals and watch in this example). In that case, should the property be called twice, or should the debugger attempt to cache the value and show that? How long does the value stay cached for – what if there is an explicit call, say, in the immediate window that affects its value? The debugger currently reevaluates properties for different views (in VS 2003 a single view, like the watch window, could actually cause a property to be evaluated many times).
I’m sure it’s becoming clear why debugger developers aren’t huge fans of implicit function evaluation (that is, function evaluation that happens without an explicit user action). It turns out that there is even more bad news. Function evaluation is slow, hundreds of times slower than simply examining a field. It’s also not guaranteed to finish executing; a property evaluation may simply spin forever. Therefore the debugger imposes a 10 second timeout at which point it attempts to abort the function evaluation. The CLR can’t actually guarantee much about function evaluation aborts, so it’s not an operation we want to happen very often. In addition, function evaluation runs in the debuggee itself (which is how it modifies the debuggee’s state). That means that when a function evaluation is occurring the debugee has an active thread. This can cause difficult to understand and debug problems, particularly in multi-threaded applications.
It may seem like I’m making a case for eliminating FuncEval (or at least implicit FuncEval) entirely. On the contrary, I’m as much to blame as anyone for the increased usage of implicit FuncEval in C# debugging in VS 2005. In VS 2005 I was the PM for the expression evaluator, which is the part of the debugger that understands a particular language (such that it’s possible to evaluate arbitrary expressions like 1+Foo() in the immediate and watch windows). A number of the features that improve data visualization require function evaluation to work. For example, the VS 2005 C# debugger will implicitly call ToString on objects that are displayed in the debugger variable windows. This often gives a significantly better immediate view of the data. Debugger visualizers and type proxies, extensible systems for providing your own data visualization also require function evaluation. Type proxies are used to greatly enhance the display for collection types like Dictionary<T> (where the underlying store isn’t even close to the client’s conceptual model).
The trick of course is to harness the power of function evaluation in such a way that for the vast majority of cases it makes it easier to debug a program instead of harder. That’s why we try to limit the number of places that we perform implicit function evaluations to methods that should not have side effects. Property evaluations are very often safe to call because a large number simply return the underlying field. ToString methods should virtually never modify the state of the object.
I’ve got a lot more to say on the subject, including common examples of performance problems that may be introduced by function evaluations, customization options, workarounds for situations where better data visualization is important but you can’t use ToString, when function evaluation doesn’t work, and of course a more convincing explanation as to why implicit function evaluation is a good thing. Unfortunately I’m exhausted so it’ll have to wait for another day…
Ever since we released the first version of C# 1.0 I’ve received a question or two a month about XML documentation comments. These are often referred to as ‘doc comments’ for short. The questions range from the use of doc comments in VS to the recommended schema of the XML. This post captures a few of the common questions that I’ve seen.
Why isn’t there a multi-line version of XML doc comments?
There are actually two forms of doc comments in C#, single and multi-line. However, the single-line version is by far the most commonly used; in fact, the multi-line version wasn’t supported until the 1.1 version of the compiler even though they were defined in the 1.0 version of the language spec. The single line version is likely to be familiar to any user of Visual Studio; it’s syntactically indicated on a line that starts with a triple slash (///):
/// <summary>
/// This is the single line version of the doc comment
/// </summary>
static void Example()
{
}
The multi-line version uses /**:
/** <summary>
* This is the multi-line version
* </summary> */
static void Example()
{
}
They are functionally identical, the only difference being that it’s possible to use the multi-line version “inline” within an expression. The multi-line version of the comments weren’t actually in the proposed version of the language specification submitted to ECMA; however, the ECMA committee decided that having both forms would be better.
The C# language service doesn’t support multi-line XML doc comments as well as the singe line comments (i.e. /** doesn’t auto-generate any tags); however, colorization for multi-line doc comments does work, and in VS 2005 it’s possible to get completion lists for the tags but you must first end the multi-line comment and then go back and enter in the tags.
How do I make VS show the XML doc comments of the types and methods of my components in completion lists and object browser?
This has been an extremely common question for a long time. The short and long of it is that you must deploy the XML file that is generated by the compiler with the component. They must be in the same directory, and the XML file must have the same name as the component except with an .xml extension. I wrote a whitepaper that contains a walkthrough, in VS 2003, which demonstrates this. It’s available here.
How do use XML doc comments to refer to generic types?
Several of the tags that are recommended in the C# language specification have an attribute named ‘cref’ on them. Cref refers to a code reference. This attribute can be used by tools to create links between different types and members (e.g. object browser uses crefs to create hyperlinks that allow quick navigation to the related type). The C# compiler actually understands the cref attribute to a limited extent. The compiler will try to bind the type or member listed in a cref attribute to a code element defined your source. Assuming that it can it will then fully qualify that member in the generated XML file. For example:
using System.Collections;
class Program
{
/// <summary>
/// DoSomething takes a <see cref="ArrayList"/>
/// </summary>
void DoSomething(ArrayList al) { }
}
This generates the following XML file:
<member name="M:Program.DoSomething(System.Collections.ArrayList)">
<summary>
DoSomething takes a <see cref="T:System.Collections.ArrayList"/>
</summary>
</member>
Notice that the compiler bound ArrayList and emitted System.Collections.ArrayList instead.
I’m sure you’re saying, wow, fascinating, um… but what does that have to do with generics? Good question. Generics complicate doc comments because C# uses angle brackets which would usually be associated with XML. It’s possible just to use the normal escaping mechanisms associated with angle brackets (> <) in XML. Unfortunately this turns out to look fairly ugly:
using System.Collections.Generic;
class Program
{
/// <summary>
/// DoSomething takes a <see cref="List<T>"/>
/// </summary>
void DoSomething(List<int> al) { }
}
This can become particularly onerous when the generic type has many type arguments. The compiler team decided to improve this by allowing an alternate syntax to refer to generic types and methods in doc comments. Specifically, instead of using the open and close angle-brackets it’s legal to use the open and close curly braces. The example above would then become:
using System.Collections.Generic;
class Program
{
/// <summary>
/// DoSomething takes a <see cref="List{T}"/>
/// </summary>
void DoSomething(List<int> al) { }
}
The compiler understands this syntax and will correctly bind List{T} to System.Collections.Generic.List<T>.
When the <example> tag is used and there are a number of generic types or methods in the example, an easier approach is to simply surround the example with a CDATA block. That way there is no need to escape less-than signs.
Which doc comment tags are understood and used by Visual Studio?
There are a number of tags that Visual Studio uses to process or present information:
|
Tag name |
Tools that make use of the tag |
|
summary |
Completion lists, quick info, class view, class designer, object browser, object test bench |
|
param |
Parameter help, quick info, object test bench, class designer, object browser |
|
exception |
Completion lists, quick info, object browser |
|
include |
C# Compiler |
|
returns |
Object browser |
|
see/seealso |
Object browser |
The ‘metadata as source’ feature, which is invoked when goto definition is performed on a type or member that is defined in metadata, processes a number of the tags documented in the C# language specification and tries to provide a reasonable view.
How do I generate HTML or .chm documentation from the XML file?
The generated XML file actually doesn’t contain enough information to fully generate good reference documentation. In fact, it was an explicit goal to make the XML file contain just enough information to map the comment back to the associated code element in metadata. Regardless, there are a number of tools that take the assembly and the generated XML file and produce a very nice looking, easy to browse output. NDoc has been a favorite of many developers that I’ve talked to for quite a while. I believe that development on NDoc has slowed somewhat; another option is SandCastle.
There was a recent post in the Visual C# IDE forum which started me thinking about navigating through a solution in Visual Studio. The post was basically asking whether it’s possible to quickly navigate to a file in your solution if you know its name. There are a few different navigational approaches that Visual Studio offers. First, if the file is already open, then it’s possible to use Ctrl+Tab or the tab channel to find the file. Ctrl+Tab functionality was actually significantly improved in VS 2005. In VS 2003 Ctrl-Tab had no UI associated with it, so it was difficult to navigate to a particular open file (though it was extremely good at quickly switching between 2 or 3 files). In VS 2005 Ctrl+Tab now shows UI that enables navigation to both open files and active tool windows. The UI is modeled after the Alt-Tab functionality in Windows:
It’s not immediately obvious the first time that the Ctrl+Tab feature is used that the arrow keys can be used to navigate among the items. For example, invoke the window by typing Ctrl+Tab. Keep the Ctrl key depressed, but release the Tab key. Now simply use the arrow keys on the keyboard to change the selection to either the file or tool window that should acquire focus.
Another option to quickly navigate to open files is the use of the file tab channel. The tab channel (the list of tabs that contain the name of the documents that are open if ‘tabbed documents’ is the current mode) also underwent an overhaul in VS 2005. The mouse is an obvious way to navigate using the channel, but there is a more keyboard focused route as well. The channel has a little chevron on the right hand side which when clicked will show a drop-down of all open documents (regardless of whether they are visible in the tab channel). This shortcut can be invoked via Ctrl+Alt+<Down Arrow>. A small drop-down appears and has focus; the arrow keys can be used to quickly select the file to make active:
The final tried and true mechanism that I’ll talk about for accessing open documents is the Windows ‘window’. It’s accessible off of the Window top level menu item (Alt-W, W). This particular navigation mechanism is useful to know because it works the same in VS 2003 and VS 2005, and it works regardless of whether environment is set to ‘tabbed documents’ or ‘multiple documents’. The accelerator key for activating the selection is “Alt+A” when the window has focus:
Of course, none of those navigation methods works if the desired file isn’t already open in the editor. There are several different ways to navigate to open a file, but the most common is probably Solution Explorer. In fact, the question referenced above was specifically about finding a way to navigate solution explorer via the keyboard (quickly) even in the face of collapsed nodes. It turns out that there really isn’t a good way to do this. However, there are a few other options for opening files that are in the solution. For example, it’s possible to quickly open a file in the solution using the find combo box that appears on the Standard toolbar. The keybinding to jump to the combo box is Ctrl+/ with the C# keybindings. Invoke this keybinding, and then type “>” to enter ‘command’ mode. Type the word ‘open’ and then start to type the name of a file in the solution. A list of files, filtered by the text already typed, appears as a dropdown:
The biggest problem with this approach is that selecting an item in the list immediately puts the full path name of that item into the combobox, which after another character is typed (like backspace) will filter the list down to only files with that path. A smaller issue is that the list also contains various other solution artifacts like the references, so the list is longer than it needs to be. The nail in the coffin for me is that it doesn’t filter based on substrings anywhere in the filename, but rather only what the filename starts with. That is, searching for Item wouldn’t return “IconicItem.cs.”
I’ve often found myself wanting a way to quickly navigate to a file that meets the following requirements:
· A modal UI that disappears as soon as I’m done using it
· A filtered list of only the files that are in my solution
· A way to quickly search through those files using a substring search
· Keyboard focused access such that I don’t need to use the mouse at all
It turns out that Visual Studio doesn’t have a tool or feature that meets all of these criteria by default. There are many that come close, like the Find Combo above, or Open File (Ctrl+O). Unfortunately none of them is exactly what I want. Luckily it’s possible to extend VS with some interesting addins. In this case I wrote a simple little navigation tool that has the following UI:
The Matching Files listbox contains all of the files in the solution, regardless of their project (or their type, notice NavigationForm.resX). The Project listbox is contextual based on the selection in the Matching Files listbox. In this case AssemblyInfo.cs occurs in the NavigationUI, FileNavigationAddin, and TypeNavigationAddIn projects. The ‘Select a file to Open’ textbox filters the list of Matching Files down based on a substring match. The up and down arrows will change the selection in the Matching Files listbox even while the ‘Select a file to open’ textbox has focus; similarly, the Shift+ the up or down arrows will change the selection in the Project listbox.
The addin works fairly well for small to medium solutions, but it takes time to appear in the face of larger solutions. Currently it enumerates the files in every project in the solution every time it is invoked. To make it faster would require keeping track of a list of files and updating them based on project events like additions, removals and renames, which I haven’t been motivated to do yet.
Regardless, the file navigation addin is available here in .vsi form (open the zip file and double-click on the .vsi). After running the VSI a new command named FileNavigationAddin.Connection.Connect.FileNavigationAddIn is added to Visual Studio. There is no default keybinding, so one must be set through the Tools | Options | Keyboard page. I override Ctrl+T because I never use Edit.TransposeCharacter.
There was an interesting post a while back in the Visual C# IDE forum asking whether or not it was possible to automatically insert company specific information every time that a new file is added to a C# project (specifically in C# Express 2005). It turns out that it is possible. The idea is to create a new item template that contains this information by default. Item templates are the name of the elements that appear in the "Add New Item..." dialog. This dialog appears when a project is right-clicked and Add New Item... is selected.
In order to illustrate this, let's take a look at two possibilities for creating a template which adds a new class. Item templates are stored in zip files. The set that are installed by default for C# Express are located at Program Files\Microsoft Visual Studio 8\Common7\IDE\VCSExpress\ItemTemplates\1033. Copy the Class.zip file, which represents the class item template, to My Documents\Visual Studio 2005\Templates\Item Templates\Visual C#\. The .zip file contains two files, a .vstemplate file and a .cs file. The .vstemplate file controls a number of the display attributes that are shown in the Add New Item dialog, such as the name of the template, the icon, the description, etc. It also contains all of the information that the project system uses to figure out which files this item template adds to the project. It is a fairly straightforward XML format. In this case, let's modify the name slightly so it's clear that the template has been added. That requires changing the line in the .vstemplate file from:
<Name Package="{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}" ID="2245" />
to:
<Name>Custom Class Template</Name>
The only other change that needs to be made is in .cs file. Near the end of the .vstemplate file there is a line that indicates the file, Class.cs, in which to 'replace parameters'. ‘Replace parameters’ indicates that the project system should do a textual replacement in the named file looking for certain strings. The project system provides some default replacement strings which are documented here. In this specific case these parameters may be enough, we simply need to modify the Class.cs file and add the company comment at the top of the file in the following format:
/*--------------------------------------------------------
* $itemname$.cs - file description
*
* Version: 1.0
* Author: Anson Horton
* Created: $time$
*
* Revisions:
* -------------------------------------------------------*/
Zip those two files back up, and verify it's in the directory mentioned above. Start up C# Express, create a project, and right-click on it to add a new item. Notice that in the lower portion of the Add New Item dialog, there is a section called "My Templates." A template called Custom Class should appear there; when that template is added to the project it will create a class with the header above (of course, you'll want to change the author name :).
The only unfortunate aspect of this approach is that it’s not possible to format the time outside of the format that is provided by default and it’s not really possible to share the template with coworkers because the user name is hard-coded into the item template. There is a slightly more complicated alternative which is simply an extension of the approach above. The project system provides a mechanism for creating 'Wizard' templates. These can be arbitrarily complicated, but in this case it would be fairly trivial. The idea is to create a class which implements the IWizard interface (defined in Microsoft.VisualStudio.TemplateWizardInterface). The majority of the methods would simply be left blank (they shouldn’t throw an exception). The only two that would need to be implemented would be RunStarted and ShouldAddProjectItem. This would look something like:
public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
{
replacementsDictionary.Add("$UserName$", Environment.UserName);
replacementsDictionary.Add("$DateTime$", DateTime.Now.ToString());
}
public bool ShouldAddProjectItem(string filePath)
{
return true;
}
This assembly is then built, signed, and installed into the GAC. The RunStarted method is simply modifying the set of template parameters that can be used in the source file (the project system uses these as lookups when it does its textual ‘replace parameters’). In this case it is possible to have the user name of the user who started C# Express as well as modifying the format of the date time. In order to tell the project system to use this wizard, the .vstemplate file is modified with a line similar to:
<WizardExtension>
<Assembly>CustomItemTemplateWizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=29ab57250b4cf6c3</Assembly>
<FullClassName>CustomItemTemplateWizard.ExampleWizard</FullClassName>
</WizardExtension>
This line appears below the TemplateContent section. After that, it's simply a matter of updating the Class.cs file to use the additional template parameters:
* Author: $UserName$
* Created: $DateTime$
There is more information about creating wizard templates here.
A recent bug entered via MSDN Product Feedback Center questions the current behavior of add new item with respect to the class template. In particular, if a class is added to a project then references to System.dll, System.Data.dll, and System.XML.dll are added automatically. This can certainly be a frustrating experience if the project doesn’t require those references and the developer is trying to keep the set of references pristine.
There are a couple of reasons for the current design. First, there is a bit of historical reasoning. Both VS 2002 and VS 2003 supported this behavior. When we authored the item templates for VS 2005 we decided that we would only make functionality changes when we had received substantial feedback that the model or content was poor. Since we didn’t have that feedback about the reference addition, we didn’t make the change. The second reason has to do with a combination of the popularity of those particular references plus the behavior of a feature that we added to C# in VS 2005 called ‘add using’. Add using will proffer a smart tag on a type which allows a using directive to be automatically added to the file/namespace that you’re working in; however, it requires that the assembly which contains that type already be referenced by the project. Given that these two assemblies contain types which are extremely common in a number of projects, we decided that continuing to add them automatically would enhance the experience. It’s also important to note that the C# compiler will not emit references to assemblies when the code that is being compiled does not contain any of the types defined in those assemblies (so there isn’t really an additional cost outside of readability for having those particular assemblies referenced).
Given that we still haven’t received a lot of feedback outside of the bug mentioned above, we’re going to keep the behavior the same for the moment (obviously if we receive more feedback we’ll reconsider). However, the good news is that it’s possible to customize this behavior fairly easily. The C# item templates that are shipped with Visual Studio are installed to %Program Files%\Microsoft Visual Studio 8\Common7\IDE\ItemTemplates\CSharp\1033. The Add | Class… template is in the Class.zip file. There are two files in the zip; the Class.cs file is the general shape of the generated file. It has a few replacement strings inside of it that are determined based on the “Root namespace” of the project, or the filename the is chosen when the class is added. The Class.vstemplate file is a description of the contents of the template, and allows the icon, description, etc. of the class template to be changed (these are displayed in the Add New Item… dialog). The .vstemplate file also contains the list of references to use when a new class is added to the project. In order to change this behavior, simply change:
<Reference>
<Assembly>System</Assembly>
</Reference>
<Reference>
<Assembly>System.Data</Assembly>
</Reference>
<Reference>
<Assembly>System.Xml</Assembly>
</Reference>
to:
<Reference>
<Assembly>System</Assembly>
</Reference>
<!--<Reference>
<Assembly>System.Data</Assembly>
</Reference>
<Reference>
<Assembly>System.Xml</Assembly>
</Reference>-->
Then re-zip the contents and close all instances of Visual Studio. Then run devenv /setup (from the %Program Files%\Microsoft Visual Studio 8\Common7\IDE directory). I didn't try it, but it should be possible to run devenv /InstallVSTemplates instead which sould execute faster. This will recreate the cache that VS uses to store the unzipped versions of the item templates. When this is done, start VS. The Add | Class… item template will no longer add a reference to System.Data.dll or System.Xml.dll to your project.
A week or so ago a developer who was starting to use VS 2005 mentioned that he was a big fan of the Class View and was disappointed to see that it was no longer possible to synchronize the Class View with the editor. In VS 2003 this was possible via a command called ‘View.SynchronizeClassView’ that appeared on the editor context menu. This command was removed in VS 2005 based off of the command usage data collected during the Beta and CTP releases, in an effort to reduce clutter.
The good news is that the command was only removed from the menu, not the product. The better news is that Visual Studio actually has an extensive mechanism for customizing layout, including the names, order, icons, etc. of commands that appear on menus.
In order to move the command back onto the editor context menu, follow these steps:
- Select Tools | Customize…
- In the tabbed dialog that appears, search through the checked list box list, on the “Toolbars” page, for an item called “Context Menus”. Check it.
This causes a new toolbar to appear at the top of the screen underneath the toolbars that are already showing. This toolbar is only visible when the Customize dialog is showing.
- Change the active page from “Toolbars” to “Commands” (on the Customize dialog)
- In the Categories list box, select View
- The right-hand pane, “Commands”, will be updated to show all of the commands prefixed with “View.” Scroll through that list and find SyncrhonizeClassView (the list is alphabetically ordered).
- Drag that command from the “Commands” list on to the Editor Context Menu | Code Window item in the toolbar that appeared when we selected “Context Menus.” You can put it wherever seems appropriate on the menu.
- Close the Customize dialog
The editor context menu now contains the command, and whenever you’re in source you can right-click and select Synchronize Class View, just as in VS 2003. This same process can be used to change the order of commands that appear on the menus, their names, the icons associated with them, their accelerator keys. You can even delete commands that are cluttering the menu that you never use. These customizations can even be preserved into a .vssettings file and shared on multiple machines or with coworkers.
The question got me thinking about Class View and how people are currently using it. I’m curious:
- Do you use Class View? (If not, do you use any tool that works like Class View, the Navigation Bar, or maybe an addin?)
- If you use Class View, do you tend to use it for navigation, browsing, or both?
- Class View used to provide a set of ‘Add’ commands for adding new methods and classes. Did you use those? Do you miss them in 2005?
- Do you like the split screen view of the Class View vs. how it appeared in VS 2003?
- What requests do you have for Class View in the future?
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="http://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.
A few months ago I was in LA speaking at PDC 2005. I was absolutely astounded by the amount of excitement that the LINQ project generated. I haven’t been working on LINQ anywhere near as much as some of the other members of the team, but I’m happy to say I got to help out a bit with some of the content that we delivered at PDC (notably the hands on labs). Outside of the LINQ project announcement the C# team actually had two additional sessions. One was on debugging ASP.NET applications, given by Habib, and the other was on Tips & Tricks for the Visual C# 2005 IDE which I gave. The presentation was a ‘lunch’ session, which is a new style of presentation that we were trying this year. The idea is that attendees sacrifice a bit of their lunch time, grab a boxed lunch, and learn about a technology that is either shipping or close to shipping (the majority of PDC content is focused on future technology). The most interesting aspect of this style of presentation is that at any point during the presentation attendees should be able to leave or enter and still end up getting useful information out of the presentation. Organizing a presentation in which it’s okay to walk in and out was a bit of a challenge, particularly since I still wanted it to flow in a meaningful way. Ultimately we decided that the vast majority of the lunch sessions should be ‘tips and tricks’ to facilitate that goal.
The IDE session went well (outside of some troubles the day before with a rogue hard drive), and a number of folks mentioned that the tips would be very useful when they got back to work and started using 2005 again. Virtually any one of the tips that I presented could actually have a blog entry of its own, and maybe if I find some time at some point I’ll actually do that; however, for the moment I wanted to post a recap of the tips that I gave for people that couldn’t attend PDC. It’s also possible to watch a recorded version of the presentation here.
The presentation is broken down into four sections which represent the various meta- tasks that happen when developing software, particularly in the edit, compile, debug cycle (we don’t include test, document, design, etc. in these presentations at the moment). The four sections are understanding, modifying, writing, and debugging code. Imagine that you’re given a new code base to work on and you’re asked to add a feature. In order to do that, you’ll probably start by spending time trying to understand what the code does and how it works. After you’ve done that, you’ll likely have to modify it in order to make the feature that you’re working on fit in the product in a natural way. Then you’ll spend some time actually writing the feature, and ultimately, unless you’ve gotten it right the first time, you’ll spend some time debugging it. I’ve listed the tips that I gave in the presentation below categorized into those four sections.
I hope they are helpful!
Understanding Code
Tip #1: Class designer
This is less of a tip and more of a suggestion. When dealing with a new code base it’s often a good idea to get a high level view of how classes interrelate. It is sometimes possible to do this through specs and architecture diagrams, but those are often out of date (if they exist at all). As an alternative, it’s possible to use the Class Designer, included in VSTS, to build up those relationships on a diagram. That diagram can then be persisted as part of the project and serve a similar purpose for anyone in the future facing the same problem. The nice thing about the Class Diagram is that it’s just a view on the source code, such that changing either the diagram or the source will update the other.
To access the Class Designer a Class Diagram must be added to the project.
- Right-click on the project node
- Select Add | New Item
- Choose "Class Diagram" which is in the bottom right
- Name the diagram and click "Add"
It’s possible to add a Class Diagram to the project from other entry points as well, including the context menu of Solution Explorer.
The Class Designer interacts with Solution Explorer, Class View, and Toolbox. Solution Explorer and Class View are the two that are most often used when understanding code; Toolbox is used when laying a diagram to generate code. You can drag types onto the diagram from Class View, which ultimately shows them as shapes. Right-clicking on the shapes gives a number of options to quickly build up an inheritance or association view of the types in the project.
For example, a simple example of the associations might look like:
In this case VectorObject is an abstract base class (so it has a dotted outline) of the Model class. The chevrons in the upper right hand corner of the shapes allow them to be expanded to show the type’s members (like Model and AnimationFrame) or collapsed to hide them (like Int32 and VectorObject). The relationship shown between Model and AnimationFrame is what’s known as a ‘collection association’ which can be used for arrays or generic collections. In this case animationSequences is a field of Model of type AnimationFrame[]. The final relationship is a simple association relationship; FramesPerSecond is of type Int32 in AnimationFrame. The interesting aspect of this one is that Int32 is actually a framework type loaded from metadata.
Tip#2: Keyboard bindings
I talk a lot about keyboard bindings in the presentation. With Visual Studio 2005 there are number of different keyboard ‘schemes’ that ship out of the box. Those keyboard schemes are initially dependent on the Settings that are chosen the very first time that Visual Studio 2005 is run. These can be reset or changes via the Tools | Import/Export Settings dialog. In this presentation the keyboard bindings to which I refer are the Visual C# keyboard bindings which were created to give a consistent set of themed bindings so that there were patterns to learn. For example, all of the tool windows in Visual Studio now have chorded keybindings that start with Ctrl+W.
So, Class View is bound to Ctrl+W, C. It’s also bound to Ctrl+W, Ctrl+C to make it easier to type.
Tip#3: Add New Item dialog selection
The Add New Item dialog (Ctrl+Shift+A) has been changed a bit from 2003. The most notable change is the addition of a number of templates. That makes it more difficult to navigate; however, the behavior has been changed to match that of explorer windows. If you simply start typing the full name of the item that you’d like to use then the selection will move there. For example, typing “C” will select “Class”, but type “Cr” will select “Crystal Report”. In 2003 the selected template was based off of the first letter of the word typed. So, typing “C” would select “Class” and typing “Cr” would select “Class” then select “Resources File.”
Tip#4: Class view customization
The Class View has additional configuration options over and above what it had in 2003. These are accessible from the toolbar:
If you’re using the next tip it’s important that “Show Project References” is checked.
Tip#5: Examining the framework
A question that we get a lot from new users of the framework is what concrete implementations exist for Stream. The Class Designer can actually help solve this type of question, and indeed, help explore the inheritance relationships in metadata a namespace at a time. This can be done by simply dragging an entire namespace from the “Project References” node in Class View onto the diagram. For example, dragging System.IO from mscorlib, would look like this (when zoomed in on the stream type):
Tip#6: Class View search
Class View has been changed significantly since VS 2003, but I think that the most useful addition is the ability to “filter” the list of types down to what is typed in the combo box at the top of the tool window. This is particularly useful when used with the keybinding Ctrl+K, Ctrl+V which puts focus in that combo box (even if you’re current focus is the editor). Then it’s as simple as typing in the name of the type to find and then hitting enter to navigate.
Tip#7: Goto definition
This is one that a lot of folks may already know from VS 2003, but it’s extremely useful so it’s worth mentioning. A number of times when trying to understand code it’s worth looking at each line of a particular method. Inevitably that method will create a new type, or call another method, etc. and it will be necessary to find out more information about them. In order to do that, you can use “Goto definition” via the keyboard binding F12 and that will navigate you to the declaration of that method, type, local, etc.
Tip#8: Collapse to definitions
A lot of times when C++ programmers start using C# they often ask for a high level view of the source code; a quick way to find out the members, fields, and such that a class has (like a header file). It’s possible to use Class View or Class Diagram to get this view, but that takes you out of source pretty quickly. An alternative is to use a command we call ‘Collapse to definitions’, which is bound to Ctrl+M, Ctrl+O. That command collapses all of the regions in the file and makes it significantly easier to skip over the implementation.
Once the file is in a collapsed state it’s often useful to simply edit a particular collapsed block. There is a “Toggle Outlining” command, which is bound to Ctrl+M, Ctrl+M, which will either expand or collapse an outlining region depending on its current state. It’s equivalent to using the little plus sign on the side of the editor, but doesn’t require using the mouse.
Tip#9: Navigation stacks
There aren’t all that many people that know Visual Studio actually has more then one navigation stack. There is a general navigation stack that is exposed by the editor. There are various heuristics that are used to track ‘interesting’ points that should be put onto that navigation stack. That navigation stack is exposed through the ‘Text Editor’ toolbar. Backward is bound to Ctrl+- and Forward is bound to Ctrl+Shift+-. This navigation stack works well in a lot of cases, but when using Goto Definition to drill in to a particular piece of code it’s often useful to irrefutably say ‘go back to the last place I invoked Goto Definition from.’ This can be accomplished by hitting Ctrl+*. This works in both 2003 and 2005. In 2005 you can also use Ctrl+& to navigate to the destination of the last Goto Definition invocation.
The ‘Goto Definition’ specific navigation stack is also, in 2005 C#, bound to extended mouse buttons on an Explorer mouse.
Tip#10: Find all references
Goto Definition is extremely useful, but it’s really only half the story. If you’re on a definition or a reference and you’d like to find all other references then it would be nice if there was a quick command to do so. In 2003 there was a command called “Goto Reference”, which many developers pointed out was pretty nonsensical since there is virtually always more then a single reference. What many developers didn’t know is that after invoking Goto Reference it was possible to iterate through the remaining references by using Ctrl+Shift+1 and Ctrl+Shift+2. To be fair, we made that pretty much impossible to discover. The second question people always ask is what reference Goto Reference picks in 2003; the answer is that it picks one at random.
In 2005 that’s changed significantly and now we show a results list when “Find All References” is invoked. The keyboard command for Find All References is Shift+F12, which makes sense since it’s the inversion of Goto Definition. The nice thing about the list is that it’s possible to do non-linear searches, and that it’s easy to see how many references actually exist.
Tip#11: List iteration
There are a number of tool windows in Visual Studio that contain lists of information. The Error List, Task List, Find Symbol Results, Find All References, and the Output Window. All of the items in these tool windows can be iterated through using F8. Imagine that you’ve just done Find All References using Shift+F12. You can then simply hit F8 from the editor and navigate to each reference, you’re focus will stay in the editor. It’s very useful for compile errors as well.
Modifying Code
Tip#12: Close all but this
In 2005 the menu for the document tabs in Visual Studio has been updated to include a command called ‘Close all but this.’ This is extremely useful if you’ve been editing code for a long while and the number of documents that you have open is large, but you still want to be editing the current file… well, you get the idea. Simply right click on the tab of the file that you want to remain open:
Copy Full Path and Open Containing Folder are also very useful if you need to do file management outside of VS.
Another quick note for the tabs is that if you middle click on the tab then it closes just that tab. It’s a more local way to close the current document then scrolling over to the [x] if you’d rather use the mouse (instead of Ctrl+F4).
Tip#13: FxCop integration
A question that we often get is whether or not there are any tools in VS 2005 that will help detect whether or not a piece of code needs to be refactored. The tool support isn’t expansive, but the integration of FxCop (a static analysis tool originally released on GotDotNet which runs over IL and reports violations of an extensible rule set) provides a good starting point. There are two rule sets that ship in the box, Naming and Maintainability, that can help identify where to use the Rename and Extract Method refactorings.
To customize the FxCop integration:
- Right-click on the project node in Solution Explorer and select Properties
- On the bottom left there will be a tab called "Code Analysis," select it
- A page that will allow the rule sets to be selected will appear on the right
To run the FXCop integration simply go to the Build menu and select:
The violations will be reported in the error list.
Tip#14: Smart tags
In VS 2005 there is a common idiom used throughout the environment that we call smart tags. They appear in the editor as small markers, orange or blue, after an action is performed which indicates a ‘next step’. For example, changing the name of an existing element will provide a smart tag marker that would perform a rename refactoring.
The tip here is simply that Ctrl+. is bound to the command that invokes the smart tag menu, so in order to perform the action it’s often as easy as hitting Ctrl+. and then <Enter>.
Tip#15: Solution explorer entry point for rename
A common style that we see C# developers’ use is to have a single type per file. If a rename refactoring is invoked from source, and you’re changing the name of the type that exists in that file, then it’s easy for these to get out of sync and for 2 steps to be taken every time the name of a type is updated. If, instead, the rename of the file is performed first then assuming that the namespace name is the same as the project (plus the folder structure) and the name of the file is the same as the name of the type, a prompt will appear allowing a rename refactoring of the type to be performed as well.
Tip#16: Keybindings for refactorings
The keybindings for refactorings use the chord Ctrl+R. So, Extract Method is bound to Ctrl+R, Ctrl+M, while Rename is bound to Ctrl+R, Ctrl+R, etc.
Writing Code
Tip#17: Window | Close all documents
In both 2003 and 2005 a quick way to close all of the documents that are open in the editor is to use the “Close all documents” command. This command is available off of the Window menu in the editor.
Tip#18: Code snippet customization
I expect that a number of people reading this already know what Code Snippets are so I won’t spend much time on them. However, the tip here is that Code Snippets are completely customizable, including the ones that are shipped in the box. In order to edit them there are two important directories. The first directory is Program Files\Microsoft Visual Studio 8\VC#\Snippets\1033\ which contains the set of Code Snippets that are shipped in the box. The Visual C# directory contains the common expansion code snippets, like ‘class’, ‘prop’, etc. The Refactoring directory contains the snippets that are used for code generation. The second interesting directory is My Documents\Visual Studio 2005\Code Snippets\Visual C#\My Code Snippets which is where we expect most snippets restricted to a single user to live.
There is a good base for the format of the snippet files here.
Tip#19: Code Snippet Insertion
There are a few ways that Code Snippets can be inserted. Either they can be inserted into the editor by simply typing the full name of the ‘shortcut’ (defined in the schema) and hitting <tab>, or by using the Code Snippet Picker which is invoked via the keyboard binding Ctrl+K, Ctrl+X, or via the Surround With command which is invoked after you’ve made a selection via the keyboard binding Ctrl+K, Ctrl+S.
After a snippet is inserted then the fields (the areas of text that have a green background) can be navigated between by using <TAB>. When you’ve finished replacing the fields with the text that you want them to have, you can use <Enter> to ‘commit’ the snippet such that the fields go away.
Tip#20: Generate method stub
The tip for generate method stub is simply that when you’re writing code it’s often a better idea to use generate method stub to get a method with a correct signature as opposed to extract method. Extract method is more useful when the code is already written and you’re simply trying to simplify different parts of it. The reason is that for the signature of extract method to be correct it needs to know the usage of the variables both before and after where the method is called, which may not be available if you’re still writing the code.
Generate method stub itself is a feature that allows a method call to be written prior to the declaration and then to have that declaration inferred from the call itself.
Tip#21: Customization of the completion list
The C# editor provides a number of customization points, but the ones that we have received the most feedback on effect completion list behavior. The tip here is simply that the customizations are available in different scenarios from the Tools | Options | Text Editor | C# | IntelliSense page:
1. The completion list appears too often and interferes with what you’re trying to type. You’d like to revert to the 2003 completion list behavior. Simply toggle this option:
2. The completion list appears when you want it to, but it completes the wrong words. This happens when the thing being typed hasn’t been declared yet. You can customize the set of characters on which the word will be inserted and the completion list dismissed with these options:
Tip#22: Add using
This tip is pretty simple. If you know the name of the type that you want to add, but you don’t have a using for it yet, simply type the name. A smart tag will appear and allow you to either fully qualify it or add it to the top of your file.
Debugging Code
Tip#23: Stepping behavior customization
It’s often the case that when debugging code you’ll end up in a situation in which you’d like to step into a method, but the argument to the method contains property evaluations. For example:
In this case Salary is a simple property which returns a backing field of the Employee. I would never want to step into that getter because it will always do exactly the same thing. It would be nice if there was a way to tell the debugger that so that you don’t have to step in, step out, and then step in again in order to get into the IncreaseSalary method.
It runs out that there are a set of attributes that can configure different aspects of debugging behavior. One of those attributes controls stepping behavior and is called System.Diagnostrics.DebuggerStepThrough(). If we were to write the code for Salary as follows:
public decimal Salary
{
[DebuggerStepThrough()]
get { return salary; }
}
The debugger will never step into it. This can be added to a property generation snippet such that every property that is generated has this behavior by default.
Tip#24: Value column customization
The value column in the debugger data windows (watch window, locals, autos, etc.) is pretty valuable space. Ideally it will always contain information that will help move you closer to figuring out what the problem with your code is, or at least the current state of your code. Unfortunately the debugger can’t always do a good job figuring out what should be displayed in that column. For example:
static void Main(string[] args)
{
List<Employee> employees = new List<Employee>();
employees.AddRange(new Employee[] {
new Employee("Anson Horton", 100.00m),
new Employee("Luke Hoban", 200.00m),
new Employee("Raj Pai", 300.00m)
});
}
Expanding employees in the watch window would appear as:
At this point if you wanted to know more information about ‘Raj’ it would be necessary to drill in to each of the items in order to find out where he’s stored (obviously in this example the list is ordered, so it’s not that complicated :-]). However, it would be nice if instead of the “{Employee}” in the value column we saw the name of the employee instead.
This is possible by overriding ToString in the Employee class. The debugger by default will evaluate ToString calls to give a better view in the value column. If, however, you’ve already overridden ToString to do something else, you don’t want to have a function call happen on your object, or you don’t have the source, there is another option.
It’s possible to use an attribute called System.Diagnostics.DebuggerDisplayAttribute to do the same thing. The attribute takes a specially formatted string. Basically any text that appears in the string will appear verbatim in the value column; however, you can use braces {} to escape expressions that should be evaluated in the context of the current object. For example:
[DebuggerDisplay("{name}")]
class Employee
{
This would modify the display above such that it appeared as:
In this case Employee has a field called name that is being evaluated (as denoted by the braces).
Tip#25: Customizing the framework types display with DebuggerDisplay
As I alluded to above it’s possible to use DebuggerDisplay on framework types as well. This is done by passing in the named parameter “Target.” There is a file called autoexp.cs that appears in the My Documents\Visual Studio 2005\Visualizers directory. This has a number of examples of how to apply DebuggerDisplay. For example:
[assembly: DebuggerDisplay(@"\{Color = {color}}", Target = typeof(SolidBrush))]
When the Target named parameter is used, the attribute should be put on the assembly as opposed to on a type. The assembly should then be built and placed into the directory mentioned above (hence autoexp.dll), which is how the debugger finds and loads it.
Tip#26: Hiding members from the debugger
When using properties that simply return a backing field a data window in the debugger often looks like this:
In this case we see name as both a property and a field. Ideally to reduce clutter and allow ourselves to find the important information faster, we wouldn’t show properties that currently only return the field. This can be accomplished using the System.Diagnostics.DebuggerBrowsable attribute. Simply put it on the member that shouldn’t be shown and pass in a state of Never:
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public string Name
{
get { return name; }
}
Now the display will appear as:
Tip#27: Datatip opacity
In VS 2005 data tips have been changed such that they can be expanded. That is, it’s possible to drill into complex objects directly in the editor instead of using only the tool windows.
The only problem is that with complex objects it can quickly become difficult to see the code underneath if you want to quickly glance at it. The tip here is to hit the Ctrl key which will make the data tips close to transparent:
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.
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()'.
Though I'm sure most everyone will be interested in my introduction (ever notice that it's hard to be sarcastic on the web?), I doubt I'll get a lot of feedback on it. So, I've decided to post a bit more today. One of the features that we continually get positive comments on is IntelliSense. More specifically, virtually every user that we talk to likes completion lists. Interestingly, when asked about it most users say that they like IntelliSense because it helps them type. However, when we do user visits (a visit where we go and watch someone use Visual Studio instead of just asking them about it) we often see that a lot of folks use IntelliSense to browse as well as to type. Of course it's completely reasonable to say that figuring out what to type is as important as the act of typing; actually, I'd argue it's significantly more important.
Regardless, it turns out that these are conflicting interests given our completion list model. Currently completion lists must contain all possible valid entries that may be typed. If it didn't then you can imagine:
- Users who are learning the language or framework getting confused on why what they see in a sample isn't available in completion lists
- The annoyance of having the editor accidentally insert code you didn't want. This can happen if what you're typing isn't in the completion list, but a proper syntactic subset of it is
The second bullet is a little hard to grasp without an example. Imagine:
class Program
{
public void StartDelayed(int delay) { }
static void Main()
{
Program p = new Program();
p.Start // cursor here
}
}
Suppose that you're trying to call a method that you haven't yet declared called Start. In any version of VS when you hit an open parenthesis and the completion list is up (it will come up initially when the dot after p is typed) then it will complete on StartDelayed, because Start is a proper syntactic subset of something that is in the list. The more items that aren't in the completion list, the more pronounced this becomes.
Of course, if the focus of IntelliSense was on browsing as opposed to typing then it's clear you would want the list to be as small as possible. Windows Forms are a good example of this. Most of the time a quick way to find the controls that you've dragged onto the form would be nice, but the completion lists don't help because they contain so many members.
In certain cases, the issue mentioned above doesn't apply. For example, if you're catching an exception then the only valid items to put in the completion list after the open parenthesis of the catch are those types that derive from Exception (or contain a nested type which derives from Exception). This type of filtering is done in Whidbey for exceptions, bases and interfaces, attributes, etc.
We'll definitely be doing more with filtering in the future. You can probably imagine a huge number of options on how to filter, and I'd love to hear feedback if you think one would be particularly useful. Here are some ideas that we have considered:
- Filter based on kind. Allow filtering down to only properties, only fields, etc.
- Filter based on when the member was introduced in the class hierarchy. For example, show only those members which were introduced in the current type first, then the next filter level shows a mix of those and the members that were introduced in base classes.
- Filter based on what's been typed so far. For example, if you have already typed 'a' then only those items that start with a would be shown.
- Filter based on security. That is, if you are requesting a particular set of permissions then only show those members which could be called in that security context.
Outside of filtering, there is also the idea of organization within the completion list. For example, the completion lists could be organized such that the members that are most commonly used are at the top. This interacts well with filtering based on what's been typed so far. In order to get the user model for filtering correct, we'll probably need to give it a lot of time to gel. It will most likely be derived from a mechanism that will allow 'escaping' out to broader contexts; for example, if something isn't currently in the list but is available without filtering being applied is typed, then the completion list would grow to include the entire context.
I am curious to hear opinions about whether you use IntelliSense more for browsing or more for typing. If more developers use it for typing, then we’d probably set our default such that the list can be filtered, but is unfiltered by default. If more developers use it for browsing, then we’d likely set it to be filtered by default.
Hello everyone,
After much abuse from my co-worker's about not sharing my daily ramblings with the world, I've decided to start a blog. I tend to be a bit cynical about web journal's, as I find it hard to believe that very many people are going to care what I'm doing on a daily basis. However, I am a terribly interesting person so maybe that'll make up for it :). It's more then likely that I'll keep posts to this blog technical.
For those of who don't know me (which is hopefully, a quickly diminishing number of you), I'm a program manager on the C# team at Microsoft. I've worked at Microsoft almost 3 years now, with 2 years of internships at MS before that. During this time, I've worked on a fair number of different areas including:
- C++ UI and Wizards
- C# Language and Compiler
- C# IDE
- C# Project System
- C# Expression Evaluation and Debugging
I'm currently involved in PMing the IDE team whose responsibilities include IntelliSense, Navigation, Parse error detection, etc. At that the heart of these features is what we refer to as the language service. The language service is responsible for any number of different tasks from the very basic colorization of code, to the much more complicated process of refactoring. I will, of course, be writing much more about each of these things in the future.