Some debugging tips on Linq.Expression

Machine Translation Warning:

The content below is mostly translated by machine, and I’ve not got enough time to review the text. So, maybe you’d like to view the Simplified Chinese version of the page.

You can generate IL code, and thereby construct a methods, using ‘System.Linq.Expression’ during runtime.Compared to ‘System.Reflection.Emit.ILGenerator’, use expression trees to construct dynamic methods is an easier way to handle IL code.As you know, IL instructions is just like any kind of assemly, which is rather elaborate. I just cannot handle it well enough 🙁

Of course, you can also use the CodeDOM to write dynamically typed (and dynamic), however, due to the CodeDOM equivalent according to the DOM tusheng code, and then invoke the compiler to compile the code, so there will be some efficiency problems (c.f. Reflection.Emit vs CodeDOM)

Fortunately,. NET Framework 4.0 starts, you can use expression trees to build dynamic methods.For example, a simple input date/time example

using System;
using System.Linq.Expressions;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Construct a DateTime function f (string prompt);
            var argPrompt = Expression.Parameter(typeof(string), "propmpt");
            Local variables.
            var localInput = Expression.Parameter(typeof(string), "input");
            var Console_WriteLine = typeof (Console). GetMethod("WriteLine", new[] {typeof (string)});
            Expression blocks.
            var expr = Expression.Block(typeof (DateTime), new[] {localInput},
                Expression.Call(Console_WriteLine, argPrompt),
                Expression.Assign(localInput, Expression.Call(typeof (Console),
                    "ReadLine", null)),
                Expression.Call(typeof (DateTime), "Parse", null, localInput));
            Based on the Lambda function expressions generated.
            var lambda = Expression.Lambda(expr, argPrompt);
            Compiling Lambda function, generate a delegate.
            var f = (Func<string, datetime="">) lambda.Compile();</string,>
            Console.WriteLine (f ("Please enter a date/time:");
        }
    }
}

Results are as follows

Please enter a date/time:
2000-1-1 PM 10:10:10
2000-1-1 22:10:10
Press any key to continue. . .

This compilation easier than manually write IL.Can be said to be a major step in a dynamic way.However, the use of dynamically generated delegate also brought some trouble for debugging.For example, as it is the code above, if we enter the date format is incorrect, then an exception is thrown:

Please enter a date/time:
test

Unhandled exception: System.FormatException: the string was not recognized as a valid DateTime.There is an unknown word (starting at index 0).
   At System.DateTimeParse.Parse (String s, DateTimeFormatInfo dtfi, DateTimeStyles styles)
   Lambda_method (Closure, String)
   At ConsoleApplication1.Program.Main (String [] args) ConsoleApplication1Program.cs: line number 25
Press any key to continue. . .

Yes, according to this tip, we can determine is ‘lambda_method’ inside an exception has occurred, in this case, because the code is simpler and therefore wrong is very easy to find out.However, when your expression tree is very complex, only know the internal exception occurs in ‘lambda_method’ is not enough.According to the existing debug tools are unable to enter the dynamic method.Therefore, only use indirect means of debugging, such as inserting a test function call:

using System;
using System.Linq.Expressions;

namespace ConsoleApplication1
{
    class Program
    {
        static void TestPoint(object v)
        {
            Console.WriteLine("Test Point : {0}", v);
        }

        static void Main(string[] args)
        {
            Construct a DateTime function f (string prompt);
            var argPrompt = Expression.Parameter(typeof(string), "propmpt");
            Local variables.
            var localInput = Expression.Parameter(typeof(string), "input");
            var Console_WriteLine = typeof (Console). GetMethod("WriteLine", new[] {typeof (string)});
            Expression blocks.
            var expr = Expression.Block(typeof (DateTime), new[] {localInput},
                Expression.Call(Console_WriteLine, argPrompt),
                Expression.Assign(localInput, Expression.Call(typeof (Console), "ReadLine", null)),
                Expression.Call(typeof (Program), "TestPoint", null, localInput),
                Expression.Call(typeof (DateTime), "Parse", null, localInput));
            Based on the Lambda function expressions generated.
            var lambda = Expression.Lambda(expr, argPrompt);
            Compiling Lambda function, generate a delegate.
            var f = (Func<string, datetime="">) lambda.Compile();</string,>
            Console.WriteLine (f ("Please enter a date/time:");
        }
    }
}

Or, we can more directly, for example, call ‘Debug.Print’.For the above code, if you receive the error input, you should have the following output:

Please enter a date/time:
test
Test Point : test

Unhandled exception: System.FormatException: the string was not recognized as a valid DateTime.There is an unknown word (starting at index 0).
   At System.DateTimeParse.Parse (String s, DateTimeFormatInfo dtfi, DateTimeStyles styles)
   Lambda_method (Closure, String)
   At ConsoleApplication1.Program.Main (String [] args) c:UsersCXYAppDataLocalTemporary ProjectsConsoleApplication1Program.cs: line number 30
Press any key to continue. . .

In fact, if we insert a breakpoint in the ‘TestPoint’ function, run to the breakpoint, you can see the following call stack:

> ConsoleApplication1.Program.TestPoint (object v) 10 C#
 	[轻量函数]	
 	ConsoleApplication1.Program.Main (String [] args) 30 C#
 	[本机到托管的转换]	
 	[托管到本机的转换]	
...

This is another function that reflects the dynamically generated really can’t debug.

But ‘Linq.Expreesion’ has taken into account the problem of debugging at design time, so introducing a secondary attribute.When you are in DEBUG mode, you can find the ‘DebugInfo’ properties in the ‘Expression’ instance.It contains the expression ‘Expression’ contains text.(C.f. debugging expression trees)

- lambda {propmpt => {var input; ... }} System.Linq.Expressions.LambdaExpression {System.Linq.Expressions.Expression<><string,System.DateTime>>}</string,System.DateTime>
+ Body {var input; ... } System.Linq.Expressions.Expression {System.Linq.Expressions.ScopeN}
		CanReduce false bool
		DebugView ". Lambda #Lambda1<System.Fun[System.String,System.DateTime]c`2>(System.String $propmpt) {rn . Block(System.String $input) {rn . Call System.Console.WriteLine($propmpt);rn $input = . Call System.Console.ReadLine();rn . Call ConsoleApplication1.Program.TestPoint($input);rn . Call System.DateTime.Parse($input)rn }rn}" string</System.Func`2>
		Name null string
		NodeType Lambda System.Linq.Expressions.ExpressionType
+ Parameters Count = 1 System.Collections.ObjectModel.ReadOnlyCollection<System.Linq.Expressions.ParameterExpression> {System.Runtime.CompilerServices.TrueReadOnlyCollection<System.Linq.Expressions.ParameterExpression>}</System.Linq.Expressions.ParameterExpression> </System.Linq.Expressions.ParameterExpression>
+ ReturnType {Name = "DateTime" FullName = "System.DateTime"} System.Type {System.RuntimeType}
		TailCall false bool
+ Type {Name = "Func'2" FullName = "System.Func'2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"} System.Type {System.RuntimeType}
+ The original view

Use a text visualizer, you can see the complete contents of the ‘DebugView’ property:

. Lambda #Lambda1<System.Func`2>(Syste[System.String,System.DateTime]m.String $propmpt) {</System.Func`2>
    . Block(System.String $input) {
        . Call System.Console.WriteLine($propmpt);
        $input = . Call System.Console.ReadLine();
        . Call ConsoleApplication1.Program.TestPoint($input);
        . Call System.DateTime.Parse($input)
    }
}

On the specific meaning of each expression in the ‘DebugView’ property, you can refer to the MSDN topics on: debugging expression trees.

Finally, XSerializer I wrote before running a test sample of the generated expression tree … …

. Lambda #Lambda1<System.Action`4>(</System.Actio[System.Xml.Linq.XElement,System.Object,Undefined.Serialization.XSerializationState,Undefined.Serialization.SerializationScope]n`4>
    System.Xml.Linq.XElement $element,
    System.Object $obj,
    Undefined.Serialization.XSerializationState $state,
    Undefined.Serialization.SerializationScope $typeScope) {
    . Block(UnitTestProject1.MyObject1 $localObj) {
        . Invoke (. Lambda #Lambda2<System.Action`1>)($obj);</Sys[System.Object]tem.Action`1>
        $localObj = (UnitTestProject1.MyObject1)$obj;
        . Call $element. SetElementValue(
            . Constant<System.Xml.Linq.XName>({http://www.yourcompany.org/schemas/undefined/1}property1),</System.Xml.Linq.XName>
            (System.Object)$localObj.Property1);
        . Call $element. Add(. Call $state. SerializeXProperty(
                (System.Object)$localObj.List1,
                . Constant<System.RuntimeType>(System.Collections.Generic.List'1),</System.RuntimeTy[System.String]pe>
                . Constant<System.Xml.Linq.XName>({http://www.yourcompany.org/schemas/undefined/1}list1),</System.Xml.Linq.XName>
                . Constant<Undefined.Serialization.SerializationScope>(System.Collections.Generic.List'1 List1 (UnitTestProject1.MyObje[System.String]ct1)))</Undefined.Serialization.SerializationScope>
        );
        . Call $element. Add(. Call $state. SerializeXProperty(
                (System.Object)$localObj.Array1,
                . Constant<System.RuntimeType>(System.Object[]),</System.RuntimeType>
                . Constant<System.Xml.Linq.XName>({http://www.yourcompany.org/schemas/undefined/1}compositeArray),</System.Xml.Linq.XName>
                . Constant<Undefined.Serialization.SerializationScope>(System.Object[] Array1 (UnitTestProject1.MyObject1))));</Undefined.Serialization.SerializationScope>
        . Call $element. Add(. Call $state. SerializeXProperty(
                (System.Object)$localObj.myObject,
                . Constant<System.RuntimeType>(System.Object),</System.RuntimeType>
                . Constant<System.Xml.Linq.XName>(myObject),</System.Xml.Linq.XName>
                null));
        . Call $element. SetElementValue(
            . Constant<System.Xml.Linq.XName>({http://www.yourcompany.org/schemas/undefined/1}myDouble),</System.Xml.Linq.XName>
            (System.Object)$localObj.Field1);
        . Call $element. SetElementValue(
            . Constant<System.Xml.Linq.XName>({http://www.yourcompany.org/schemas/undefined/2}now),</System.Xml.Linq.XName>
            (System.Object)$localObj.Field2);
        . Call $element. SetElementValue(
            . Constant<System.Xml.Linq.XName>(Field3),</System.Xml.Linq.XName>
            (System.Object)$localObj.Field3);
        . Call $element. SetElementValue(
            . Constant<System.Xml.Linq.XName>(nullableInt),</System.Xml.Linq.XName>
            (System.Object)$localObj.Field4);
        . Call $element. SetAttributeValue(
            . Constant<System.Xml.Linq.XName>(myGuid),</System.Xml.Linq.XName>
            (System.Object)$localObj.Field5);
        . Call $element. Add(. Call $state. SerializeXProperty(
                (System.Object)$localObj.AnotherObject,
                . Constant<System.RuntimeType>(UnitTestProject1.MyObject1),</System.RuntimeType>
                . Constant<System.Xml.Linq.XName>({http://www.yourcompany.org/schemas/undefined/1}AnotherObject),</System.Xml.Linq.XName>
                null))
    }
}

. Lambda #Lambda2<System.Action`1>(System.O[System.Object]bject $obj) {</System.Action`1>
    . Call System.Diagnostics.Debug.Assert(. Call (. Constant<><>c__DisplayClassb>(Undefined.Serialization.XSerializerBuilder+<>c__DisplayClassb).t). IsInstanceOfType($obj)
    )
}

Leave a Reply

Your email address will not be published. Required fields are marked *

ERROR: si-captcha.php plugin: GD image support not detected in PHP!

Contact your web host and ask them to enable GD image support for PHP.

ERROR: si-captcha.php plugin: imagepng function not detected in PHP!

Contact your web host and ask them to enable imagepng for PHP.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Content is available under CC BY-SA 3.0 unless otherwise noted.