January, 2009

  • Kirill Osenkov

    ForEach

    • 13 Comments

    In my recent post about coding styles one particular thing provoked the majority of feedback and discussions: the ForEach extension method on IEnumerable<T>. Justin Etheredge has a good post about this method here. StackOverflow.com also has a good question: Why is there not a ForEach extension method on the IEnumerable interface?

    Note: If you’d like this method to be added to .NET 4.0, go vote here: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=279093

    Recently I also went ahead and logged a suggestion against the BCL team to add this method in .NET 4.0, and got some feedback from Melitta Andersen and Justin van Patten. Essentially, the BCL team’s considerations boil down to what we’ve discussed in my recent post:

    • it encourages state mutation
    • it’s hard to set a breakpoint inside the lambda/delegate body (and other debugging issues)
    • it’s not clear where to place this method (it’s not exactly part of LINQ, it’s just a helper on IEnumerable<T>), also it doesn’t allow to chain calls

    Mads Torgersen (representing the language design team) was also reluctant about adding this method because of similar concerns (functional impurity etc). I myself in my previous post was enumerating various downsides of using this method.

    And still I think we should add it.

    My thinking is the following. The ultimate purpose of the BCL is to help avoid code duplication by introducing a common reusable set of functionality so that people don’t have to reinvent the wheel by writing their own collections, sorters, etc. A lot of people will use ForEach anyway, and if we don’t provide it in the framework, they will have to re-implement it in every new project. Also, by providing the ForEach method out of the box, we’re not forcing anyone to actually go ahead and use it – people will still have the choice and be warned about the downsides of ForEach. It’s just when they will use it anyway (and this happens a lot), they will be able to consume a ready-made one. The use of having it (in my opinion) by far overweights the downsides of using it inappropriately.

    ForEach looks really good with very simple snippets, such as:

    myStrings.ForEach(Console.WriteLine);

    Some developers forget that you can use this shorter syntax instead of:

    myStrings.ForEach(s => Console.WriteLine(s));

    Another ForEach advantage is that it allows you to extract the body of the loop into a separate place and reuse it by just calling into it.

    Also, given the fact that List<T> already has it, it seems unfair that IEnumerable<T> doesn’t. This is an unnecessary limitation (that’s what I think).

    Chris Tavares says:

    I suspect the reason that this didn't exist before is that you can't use it from VB. VB only support lambda expressions, while the foreach method requires a lambda *statement*.

    Well the good news is that we are introducing statement lamdbas in VB 10.0 so this shouldn’t be an issue at all.

    I’m also pretty sure that one can also overcome the debugging difficulties of ForEach with tooling support, such as [DebuggerStepThrough] and “Step Into Specific”. Debugging problems are not language/libraries problem per se, it’s a tooling problem, and tooling should always be fixed/improved to satisfy languages and libraries.

    A lot of people are asking for the ForEach extension method – this is probably one most wanted piece of API:

    My questions for you folks are:

    1. What do you think? Should this method be added to BCL?
    2. If yes, where? System.Linq.Enumerable? System.Collections.Generic.Extensions? Anywhere else?

    There are also variations on this extension method:

    • returning IEnumerable<T> to allow the ForEach calls to chain
    • accepting an Action<T, int> where the second parameter is the index of an item
    • Kevin’s Apply method

    If you ask me, only the simplest overload should be added, because Select is a better choice for chaining calls. Also this would encourage people to use the method in the simplest scenarios, where the downsides of doing so are negligible.

    Finally, one argument I have is that googling “foreach extension method” yields 1 590 000 results, which I think is a pretty good indication that the feature has high demand.

  • Kirill Osenkov

    How to disable optimizations during debugging

    • 2 Comments

    Sooner or later you may run into a situation where you need to evaluate a local variable under debugger and all you get is this:

    "Cannot obtain value of local or argument 'whatever' as it is not available at this instruction pointer, possibly because it has been optimized away'.

    Well, it turns out there are two different tricks to solve such problems:

    1.

    Shawn Burke blogs about How to disable optimizations when debugging Reference Source. In a nutshell, you need to:

    1. Start VS with the Environment Variable COMPLUS_ZapDisable=1
    2. Disable the VS Hosting Process (.vshost.exe) before you start debugging

    2.

    Another tip is from our VB IDE Dev Jared Parsons: Disabling JIT optimizations while debugging. Essentially, Jared points to create an .ini file with the same name as the application's .exe:

    [.NET Framework Debugging Control]
    GenerateTrackingInfo=1
    AllowOptimize=0

    He also points to the MSDN article http://msdn.microsoft.com/en-us/library/9dd8z24x.aspx (Making an Image Easier to Debug).

    To be frank, this tip didn't work for me for some reason, but I guess it's still worth mentioning.

    Hope this helps!

  • Kirill Osenkov

    Call Hierarchy Navigation in Visual Studio 2010

    • 31 Comments

    We're currently designing a new IDE feature named Call Hierarchy. Essentially, it allows you to find places where a given method is called, which is similar to how Find All References currently works. However, unlike Find All References, the Call Hierarchy feature provides more deep understanding and more detailed information about calls.

    Invocation

    You can invoke the Call Hierarchy toolwindow by right-clicking on a method, property or constructor name in the code editor and choosing View Call Hierarchy from the context menu:

    image

    Tool window

    A toolwindow will appear docked on the bottom of the Visual Studio window:

    image

    You can expand the node for the method to see information about it: incoming calls to the method ("Calls To") and outgoing calls ("Calls From"):

    image

    Here's how it works. A method (or a property, or a constructor) is displayed as a root in the treeview. You can expand the node to get a list of "search categories" - things you want to find. Four search categories are currently supported:

    1. Calls To - "incoming" calls to this member
    2. Calls From - "outgoing" calls mentioned in this member's body
    3. Overrides - available only for abstract or virtual members
    4. Implements - finds places where an interface member is implemented

    When you expand a search node (such as Calls To 'GetCallableMethods'), a solution-wide search is started in the background and the results appear under the Calls To folder. You can click on a result, and the details will appear in the Details list view on the right hand side.

    The Details list view shows all the exact call sites and locations in code where GetCallableMethods is called from GenerateXsdForComplexTypes. We see that the method is being called only once, the line of code is shown, as well as file name and position in the file. Double-clicking on that call site will navigate to it in the code editor.

    The advantages of Call Hierarchy compared to Find All References is that it allows you to explore and drill deep multiple levels into the call graph (find caller's caller etc.) Also, Call Hierarchy has deeper and more fine-granular understanding of the source code - while Find All References just finds the symbols, Call Hierarchy differentiates abstract and virtual methods, interface implementations, actual calls from delegate creation expressions, etc. Also it works like a scratch-pad: you can add any member as another root-level item in the call hierarchy tool window and have several members displayed there at once. Finally, the Details Pane given information about the concrete call sites, if a method is being called several times in the body of the calling method.

    Toolbar

    In the toolbar you can select the scope of the search: search in currently opened file only, current project or the entire solution.

    Refresh button re-fills the treeview in case the original source code was modified.

    If a root node of the treeview is selected, the "Delete Root" button will remove it from the treeview. You can add any member as a new root in the treeview by right-clicking on it in the context menu:

    image

    or adding it from the source code as described in the beginning.

    Finally, the Toggle Details Pane button shows or hides the details pane.

    Some design issues and implementation details

    Although the feature is already implemented and if you have the Visual Studio 2010 CTP, you can already play with Call Hierarchy, we're still not quite happy with the current UI design, usability and the user experience.

    For example, one issue that we're seeing is that it takes 2 mouseclicks and 2 mousemoves to invoke the Find All References search, but it takes 4 mouseclicks and 4 mousemoves to get the callers list for a given method (1 click - menu invocation, 1 click - menu item selection, 1 click - expand the treeview node for the method, 1 click - expand the "Calls To" folder). Although the search itself will be slightly faster than Find All References, the perceived complexity of invoking the feature is something we definitely want to improve. We want this feature to be at least as good and usable as Find All References, but also provide additional benefits, otherwise people will just not use the feature and continue using Find All References.

    I think I'll stop for now and see what kind of feedback you guys might have about this. In the next blog post I plan to share more of our current thinking and what we'd like to change. For now, I'd be really interested to know what you think and if you have any suggestions or ideas. Now it's not too late, and we can change the feature based on your feedback.

Page 1 of 1 (3 items)