为什么拉姆达EX pressions不与QUOT;实习"?不与、拉姆、pressions、EX

2023-09-03 05:52:58 作者:喂、不爱就让开点

字符串是引用类型,但它们是不可变的。这使得他们成为的实习的编译器;到处都是相同的字符串出现,同一个对象可以被引用。

Strings are reference types, but they are immutable. This allows for them to be interned by the compiler; everywhere the same string literal appears, the same object may be referenced.

代表也不可变的引用类型。 (使用 + = 运营商添加的方法到多播委托构成的任务的;这不是可变性)和一样,串,还有一个文字的方式重新present在code的委托,利用拉姆达EX pression,例如:

Delegates are also immutable reference types. (Adding a method to a multicast delegate using the += operator constitutes assignment; that's not mutability.) And, like, strings, there is a "literal" way to represent a delegate in code, using a lambda expression, e.g.:

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

这句话的右手边是一个前pression类型为 Func键&LT; INT&GT; ;但无处我是明确地调用 Func键&LT; INT&GT; 构造函数(也不是隐式转换发生的事情)。因此,我认为这是一个基本的文字的。难道我错了我的定义直译在这里?

The right-hand side of that statement is an expression whose type is Func<int>; but nowhere am I explicitly invoking the Func<int> constructor (nor is an implicit conversion happening). So I view this as essentially a literal. Am I mistaken about my definition of "literal" here?

无论如何,这是我的问题。如果我有两个变量,说了, Func键&LT; INT&GT; 类型,而我给你相同的λEX pressions双方:

Regardless, here's my question. If I have two variables for, say, the Func<int> type, and I assign identical lambda expressions to both:

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

...什么是$ P $从治疗这些为同一函数功能:LT pventing编译器; INT&GT; 对象

我问,因为的C# 4.0语言规范明确规定:

I ask because section 6.5.1 of the C# 4.0 language specification clearly states:

转换   匿名函数具有相同的   (可能为空)组捕获外层的   变量实例在同一   委托类型允许(但不   所需)返回相同的委托   实例。术语语义   相同在这里用来指   执行的匿名函数   将在所有情况下,产生相同的   效果给予相同的参数。

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.

这让我感到惊讶,当我看到了。如果这种行为是明确的允许的,我本来期望它付诸实施。但它似乎不是。这实际上已经得到了很多开发商陷入困境,尤其。当拉姆达EX pressions已成功地用于附加的事件处理程序,而不能删除它们。例如:

This surprised me when I read it; if this behavior is explicitly allowed, I would have expected for it to be implemented. But it appears not to be. This has in fact gotten a lot of developers into trouble, esp. when lambda expressions have been used to attach event handlers successfully without being able to remove them. For example:

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

是否有任何理由对语义上相同的匿名此行为有一名代表实例的方法,没有实现?

推荐答案

你错称之为文字,海事组织。这只是一个前pression这是转换为委托类型。

You're mistaken to call it a literal, IMO. It's just an expression which is convertible to a delegate type.

现在作为实习的一部分 - 一些拉姆达EX pressions的在的缓存,在一个单一的λEX pression,有时单个实例可以创建和重用然而,往往是线code遇到。有些人不及时治疗的方式:它通常取决于拉姆达EX pression是否捕捉任何非静态变量

Now as for the "interning" part - some lambda expressions are cached , in that for one single lambda expression, sometimes a single instance can be created and reused however often that line of code is encountered. Some are not treated that way: it usually depends on whether the lambda expression captures any non-static variables (whether that's via "this" or local to the method).

下面是这个缓存的例子:

Here's an example of this caching:

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

在这种情况下,我们可以看到,第一个动作是缓存(或至少两个的等于的代表们产生,而事实上反射表明,它真正的是的缓存在静态字段)。第二个操作创建动作的两个不相等的情况下,两个呼叫 GetSecondAction ,这就是为什么第二个是非-null在端

In this case we can see that the first action was cached (or at least, two equal delegates were produced, and in fact Reflector shows that it really is cached in a static field). The second action created two unequal instances of Action for the two calls to GetSecondAction, which is why "second" is non-null at the end.

实习中出现在code不同的地方,但具有相同的源$ C ​​$ c是另一回事lambda表达式。我怀疑这将是非常复杂的正确做到这一点(毕竟同出一源$ C ​​$ C可能意味着在不同的地方不同的东西),我肯定不会要的依赖的它正在发生。如果它不将是值得依靠,这是一个大量的工作,以得到正确的编译器团队,我不认为这是他们可以花费自己的时间的最好方式。

Interning lambdas which appear in different places in the code but with the same source code is a different matter. I suspect it would be quite complex to do this properly (after all, the same source code can mean different things in different places) and I would certainly not want to rely on it taking place. If it's not going to be worth relying on, and it's a lot of work to get right for the compiler team, I don't think it's the best way they could be spending their time.