4

我有这个 c# 代码:

Console.Writeline("Hello World");

如果我想使用 DLR 表达式执行此操作,它看起来像这样:

MethodInfo method = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(String) });
Expression callExpression = Expression.Call(null, method, Expression.Constant("Hello   World"));
Action callDelegate = Expression.Lambda<Action>(callExpression).Compile();
callDelegate();

我从 .NET 4 中的 Pro DLR 一书中举了这个例子。我不明白我们为什么要做这些额外的工作?Book 说原因是一旦代码被表示为内存中的对象,它就比 IL 指令更容易分析。

最让我困惑的是:如果我在我的代码中放置 DLR 表达式而不是 ConsoleWriteline() 方法并运行我的控制台应用程序,我将得到相同的 .exe 文件(包含 CIL 代码),我将得到写在由于 .exe 文件正在执行而导致的控制台。因此,在这两种情况下,我都会得到执行的 .exe 文件(cil 代码),但我看不到那些在运行时将代码表示为数据的对象在哪里以及如何访问它们?

4

3 回答 3

3

基本上,第二个代码片段所做的是将调用封装为表达式树。表达式树对于 .NET 来说相对较新(它们是实现 Linq 与内存中对象以外的数据机制的互操作所必需的),并将程序指令封装在可变但仍可执行的形式中。

如果需要,一旦有了表达式,就可以通过更改 Call 节点引用的 Constant 节点的值,将要输出的文本从“Hello World”更改为“Hello Dolly”。您可以更改 Call 节点以使用不同的 MethodInfo,例如调用 Debug.WriteLine() 或您开发的自定义 WriteToLog() 方法。您还可以传递该表达式,保存它,序列化它,并在比这个简单示例更远的地方调用它。所有这些更改和决策都可以在运行时根据编译时未知的信息进行。动态构造的表达式树可以基于文件或数据库中的数据创建,易于更改,不需要发布包含该行的新版本的DLL或EXE。

相比之下,对 Console.WriteLine() 的“静态”调用只能在编译时更改(尽管可能会发出一些非常混乱的 IL 动态代码),如果该字符串的写入位置有要求,则需要进行此类更改改变。

于 2011-03-17T16:28:29.380 回答
1

顺便说一句,生成表达式的另一种方法是:

Expression<Action> e=()=>Console.WriteLine("Hello World");

这将使您不必编写样板反射代码。

于 2011-03-21T08:59:02.297 回答
1

我看不到在运行时将代码表示为数据的那些对象在哪里,我如何访问它们?

它将Expression代码表示为数据:它表示以" 作为唯一参数 的Console.WriteLine 方法调用。"Hello World

下面是一个在运行时发现这个事实的例子:

var method = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(String) });
var callExpression = Expression.Call(null, method, Expression.Constant("Hello   World"));

var expression = Expression.Lambda<Action>(callExpression);  

// Now let's try to inspect 'expression'

var body = expression.Body as MethodCallExpression;

if (body != null)
{
    Console.WriteLine("Expn's body is a method-call expn.");    
    Console.WriteLine("...that calls:" + body.Method.Name);

    var args = body.Arguments;

    if (args.Any())
    {
        Console.WriteLine("The call has arguments.");    

        var firstArg = args.First() as ConstantExpression;

        if (firstArg != null)
        {
            Console.WriteLine("The first argument is a constant expn.");
            Console.WriteLine("...with value " + firstArg.Value);
        }
    }
}
于 2011-03-17T16:22:57.980 回答