c# - 为什么 lambda 表达式不是 "interned"?

标签 c# .net lambda delegates expression

字符串是引用类型,但它们是不可变的。这允许它们被编译器 interned;在任何地方出现相同的字符串文字,都可能引用相同的对象。

委托(delegate)也是不可变的引用类型。 (使用 += 运算符向多播委托(delegate)添加方法构成赋值;这不是可变性。)而且,与字符串一样,有一种“文字”方式来表示代码中的委托(delegate),使用一个 lambda 表达式,例如:

Func<int> func = () => 5;

该语句的右侧是一个类型为 Func<int> 的表达式;但我没有在任何地方明确调用 Func<int>构造函数(也不会发生隐式转换)。所以我认为这本质上是一个字面意思。我在这里对“文字”的定义有误吗?

无论如何,这是我的问题。如果我有两个变量,例如 Func<int>类型,我将相同的 lambda 表达式分配给两者:

Func<int> x = () => 5;
Func<int> y = () => 5;

...是什么阻止了编译器将它们视为相同的 Func<int>对象?

我问是因为 C# 4.0 language specification 的第 6.5.1 节明确指出:

Conversions of semantically identical anonymous functions with the same (possibly empty) set of captured outer variable instances to the same delegate types are permitted (but not required) to return the same delegate instance. The term semantically identical is used here to mean that execution of the anonymous functions will, in all cases, produce the same effects given the same arguments.

当我读到它时,这让我感到惊讶;如果此行为被明确允许,我会期望它会被实现。但它似乎不是。这实际上让很多开发人员陷入困境,尤其是。当已使用 lambda 表达式成功附加事件处理程序而无法删除它们时。例如:

class EventSender
{
    public event EventHandler Event;
    public void Send()
    {
        EventHandler handler = this.Event;
        if (handler != null) { handler(this, EventArgs.Empty); }
    }
}

class Program
{
    static string _message = "Hello, world!";

    static void Main()
    {
        var sender = new EventSender();
        sender.Event += (obj, args) => Console.WriteLine(_message);
        sender.Send();

        // Unless I'm mistaken, this lambda expression is semantically identical
        // to the one above. However, the handler is not removed, indicating
        // that a different delegate instance is constructed.
        sender.Event -= (obj, args) => Console.WriteLine(_message);

        // This prints "Hello, world!" again.
        sender.Send();
    }
}

这种行为(语义相同的匿名方法的一个委托(delegate)实例)没有实现的原因是什么?

最佳答案

IMO,您错误地称其为字面意思。它只是一个可转换为委托(delegate)类型的表达式。

现在关于“实习”部分——一些 lambda 表达式缓存了,因为对于一个 lambda 表达式,有时可以创建和重用一个实例,但是经常遇到那行代码.有些不是这样处理的:它通常取决于 lambda 表达式是否捕获任何非静态变量(无论是通过“this”还是方法的本地变量)。

这是这种缓存的一个例子:

using System;

class Program
{
    static void Main()
    {
        Action first = GetFirstAction();
        first -= GetFirstAction();
        Console.WriteLine(first == null); // Prints True

        Action second = GetSecondAction();
        second -= GetSecondAction();
        Console.WriteLine(second == null); // Prints False
    }

    static Action GetFirstAction()
    {
        return () => Console.WriteLine("First");
    }

    static Action GetSecondAction()
    {
        int i = 0;
        return () => Console.WriteLine("Second " + i);
    }
}

在这种情况下,我们可以看到第一个操作被缓存了(或者至少产生了两个相等委托(delegate),事实上 Reflector 显示它确实被缓存了在静态字段中)。第二个操作为 GetSecondAction 的两次调用创建了两个不相等的 Action 实例,这就是为什么“second”最后不为空的原因。

在代码中出现在不同位置但具有相同源代码的 lambda 是另一回事。我怀疑正确地执行此操作会非常复杂(毕竟,相同的源代码在不同的地方可能意味着不同的事情),而且我当然不想依赖它的发生。如果它不值得依赖,编译器团队需要做大量工作,我认为这不是他们打发时间的最佳方式。

关于c# - 为什么 lambda 表达式不是 "interned"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4807808/

相关文章:

function - Coffeescript中的多行函数调用

c++ - lambda函数与gsl的数值积分

c# - Automapper 可以映射分页列表吗?

c# - 如何分解 try/catch 代码?

c# - 使用未知名称/参数数量更新 sql 语句

c# - 将控制台输出镜像到文件

python - 在 Python 中使用 lambda 时遇到问题

c# - SharpPcap 4.2 与 WinPcap 4.1.3 给出 "Unable to open offline adapter: bad dump file format"

C# 使用 DateTime.TryParseExact() 多种格式解析 DateTime

c# - 远程服务器返回错误 : 227 Entering Passive Mode (500 oops vs_utility_recv_peek: no data)