C# 如果使用从方法返回的lambda,为什么事件取消订阅起作用(“not leak”)?

C# 如果使用从方法返回的lambda,为什么事件取消订阅起作用(“not leak”)?,c#,lambda,memory-leaks,event-handling,C#,Lambda,Memory Leaks,Event Handling,有一个包含事件的简单类: public class BaseEventProvider { public event EventHandler SomeEvent; public void Publish() { SomeEvent?.Invoke(this, null); } } 我知道,如果您使用lambda函数处理事件,并且在大多数情况下会导致内存泄漏,则很难取消订阅事件。 例如: BaseEventProvider eventProvider

有一个包含事件的简单类:

public class BaseEventProvider {
    public event EventHandler SomeEvent;

    public void Publish() {
        SomeEvent?.Invoke(this, null);
    }
}
我知道,如果您使用lambda函数处理事件,并且在大多数情况下会导致内存泄漏,则很难取消订阅事件。 例如:

BaseEventProvider eventProvider = new BaseEventProvider();
eventProvider.SomeEvent += (s, e) => {
     Console.WriteLine("was handled!");
};
但为什么在这种情况下取消订阅有效:

class Program {
    static void Main(string[] args) {
        BaseEventProvider eventProvider = new BaseEventProvider();
        eventProvider.SomeEvent += Handler();
        eventProvider.Publish();

        eventProvider.SomeEvent -= Handler();
        eventProvider.Publish();

        Console.ReadKey();
    }

    private static EventHandler Handler() {
        return (s, e) => {
            Console.WriteLine("was handled!"); 
        };
    }
}
控制台输出显示事件只处理了一次:

was handled!

可能是在编译和代码优化之后,有什么秘密吗

由于每个lambda表达式返回不同的代码堆,因此它具有不同的
MethodInfo
数据,您可以像中一样提取这些数据。因此,这是两个不同的方法地址,您试图从lambda表达式中取消订阅将毫无用处,因为您将提供不同的“方法”。同时,从方法取消订阅引用相同的
MethodInfo
(对象相等),并将成功取消订阅。

tldr;这不是“内存泄漏”,只是试图删除不存在的处理程序的代码。事件的“内存泄漏”是由于事件/生产者保留对订阅者的强引用。您可以使用
Handler()
取消订阅,因为您已使用相同的引用订阅。例如
Handler()==Handler()==true
user2864740谢谢您的评论。我理解为什么它能像你说的那样与方法组一起工作,因为事件对方法有很强的引用,比如(私有void处理程序(objectsender…),但是当我们执行返回lambda的方法(ananonymous方法)时,为什么unsibscription能工作呢?@user2864740 when
Handler()
被调用以取消订阅它是取消订阅。它打印出
“已处理”
仅一次。@user2864740是的,它不必是静态的,仍然可以工作,因为每个lambda表达式返回不同的代码堆。您能为此提供参考吗?AFAIK,C#规范允许用相同的方法实现两个语义等价的lambda表达式。语义相同的匿名函数与允许(但不是必需的)相同委托类型的相同(可能为空)捕获的外部变量实例集返回相同的委托实例。@PetSerAl如果我理解正确,是的,这是允许的,但这些表达式被视为不相等(由于安全原因不可运行)你怎么知道它们被视为不相等?因为句柄是不同的?但它们被允许(但不是必需的)相同。当前的C#编译器可能会为每个lambda发出单独的方法,但它不要求所有未来的编译器都这样做。