“代码即数据”在哪里?在 DLR 表达中?

发布于 2024-10-24 16:25:32 字数 749 浏览 5 评论 0原文

我有这个 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 书中获取了这个示例。而且我不明白为什么我们要做这些额外的工作?书上说,原因是一旦代码在内存中表示为对象,它就比 IL 指令更容易分析。

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

I have this c# code:

Console.Writeline("Hello World");

If I want to do this with DLR expression it looks something like this:

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();

I took this example from the book Pro DLR in .NET 4. And I don't understand why are we doing this extra work? Book says the reason is that once code is represented as objects in memory it is far easier to analyze than IL instruction..

What confuses me the most:
If I put DLR expression instead of ConsoleWriteline() method in my code and run my console applicaton I will get the same .exe file (that contains CIL code) and I will get "Hello world" written in the Console as a result of .exe file being executed. So in both cases I get .exe file (cil code) that is executed, and I can not see where are those objects that represents code as data at the run time and how can I access them?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我喜欢麦丽素 2024-10-31 16:25:32

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

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

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

Basically, what the second code snippet is doing is encapsulating the call as an expression tree. Expression trees are relatively new to .NET (they were necessary to implement Linq interop with data mechanisms other than in-memory objects), and encapsulate program instructions in a mutable, but still executable form.

If you wanted, once you had the expression, you could change the text to be output from "Hello World" to "Hello Dolly" by changing the value of the Constant node referenced by the Call node. You could change the Call node to use a different MethodInfo, for instance a call to Debug.WriteLine() or a custom WriteToLog() method you develop. You can also pass that expression around, save it, serialize it, and call it much further down the line than this simple example. All of these changes and decisions can be made at runtime based on information that is not known at compile-time. A dynamically-constructed expression tree can be created based on data in a file or the database, which is easy to change and doesn't require releasing a new version of the DLL or EXE containing this line.

By contrast, the "static" call to Console.WriteLine() can only be changed at compile-time (notwithstanding the possibility of some VERY messy IL-emitting dynamic code), requiring such a change if the requirements for where that string is written change.

月牙弯弯 2024-10-31 16:25:32

我看不到那些物体在哪里
将代码表示为数据
运行时以及如何访问它们?

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);
        }
    }
}

I can not see where are those objects
that represents code as data at the
run time and how can I access them?

It's theExpression that represents code as data: it represents a call to the Console.WriteLine method with "Hello World" as the only argument.

Here's an example of discovering this fact at run-time:

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);
        }
    }
}
尛丟丟 2024-10-31 16:25:32

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

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

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

Just as an aside, another way to generate the expression is:

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

This will save you having to write the boilerplate reflection code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文