다음을 통해 공유


Debugging and Delayed Execution in C# 3.0

Overview:

C# 3.0 added a few constructs like queries which are delay executed. This means that they are not actually executed until the results of the query are required. Debugging some of them can seem strange since one can't step in to the Query where its created but only where its enumerated, like in a foreach loop. In this article i will show some of the problems that the user can face based on the fact that the Query is delay executed and the debugger tries to be as non-intrusive as possible.

So what does it mean to step over the creation of the query, does it mean the statements that are part of the Query are executed... as you would have guessed No. At this point we have just created delegates that point to code in the Query(the body of the select, where etc), these delegate form the arguments to the extension methods (select , where etc )that form the query.

Consider the following example:

Define a Class Library and add a single extension method to it

using System;
using System.Linq;

public static class Extensions
{
    public static string EM(this UInt32 i)
    {
        return "hello";
    }
}

Now create a console application and add the class lib as a reference. In the console application add the following code ..

using System;
using System.Linq;

namespace lateboundExtensionmethods
{
    class Program
    {
        static void Main(string[] args)
        {
            var q = from i in new uint[] { 1 }
                 select i.EM();                //module containing EM is not loaded

            System.Diagnostics.Debugger.Break();  
            foreach (var v in q)           //module containing EM is loaded
                Console.WriteLine(v);
        }
    }
}

Now run to the breakpoint and try and evaluate i.EM() in the watch window.... we get an error like

 

What was all that about .... have i not just compiled and ran this code ... What's the debugger smoking ?

The Problem:

Though the problem is not specific to Extension methods. The Query rewrite rules move code in the select, where etc in to a delegate and in order to do that a new static method is generated. Until this Method is Jitted the assemblies referenced in it are not loaded. Since the assembly is not yet loaded in the debugging session the Expression evaluator can't find the methods, if they are added to watch. This leave the user feeling like he has stepped over a code that can't be evaluated in the debugger. once we come to the foreach loop the code in the select is actually need to be stepped into and the Jitter jits this method, this results in all the assemblies referenced in to be loaded.

On the other hand trying something like this, and the extension method in watch will work.

using System;
using System.Linq;

namespace lateboundExtensionmethods
{
    class Program
    {
        static void Main(string[] args)
        {
            uint j = 10;
            System.Diagnostics.Debugger.Break(); //module containing EM is loaded
            j.EM();

            System.Diagnostics.Debugger.Break();

        }
    }
}

Workarounds:

So while debugging you code if you are unable to execute an static or extension method defined in a satellite assembly check the modules window to see if the assembly is loaded, if not for the debugging session you might want to add something like typeof(className) to you'r code. Where className is the class defined in the satellite assembly.

kick it on DotNetKicks.com

Comments

  • Anonymous
    July 16, 2007
    You've been kicked (a good thing) - Trackback from DotNetKicks.com