如何在C#中正确释放匿名委托/闭包?

3
我正在开发一个GUI应用程序,它在很大程度上依赖于Action<>委托来自定义UI工具的行为。我想知道我们采用的方式是否存在潜在问题,例如实现是否保留对捕获变量、声明委托的类实例等的引用?
假设我们有这个类MapControl,它包装了一个有状态的GUI控件。地图有不同种类的工具(绘图、选择等),由ITool接口表示。您可以使用StartTool()设置工具,但一次只能有一个工具处于活动状态,因此当设置另一个工具时,前一个工具将使用StopTool()停止。当工具停止时,将执行调用者指定的回调委托。
public class MapControl
{
    ITool _currentTool;
    Action<IResult> _onComplete;

    public void StartTool(ToolEnum tool, Action<IResult> onComplete) {

        //If tool is active, stop it first
        if (_currentTool != null) StopTool();

        _onComplete = onComplete;

        //Creates a tool class, etc.
        _currentTool = CreateTool(tool) as ITool;
    }

    public void StopTool() {

        //Execute callback delegate
        IResult result = _currentTool.GetResult();
        if (_onComplete != null)
            _onComplete(result);

        //Nix the references to callback and tool
        _onComplete = null;
        _currentTool = null;
    }
}

在应用程序的 ViewModel 类中,我们设置了一些工具,如下所示:
class ViewModel
{
    private MapControl _mapControl = new MapControl();
    public void SetSomeTool() 
    {
        //These variables will be captured in the closure
        var someClassResource = this.SomeClassResource;
        var someLocalResource = new object();

        //Start tool, specify callback delegate as lambda
        _mapControl.StartTool(ToolEnum.SelectTool, (IResult result) => {

            //Do something with result and the captured variables
            someClassResource.DoSomething(result, someLocalResource);
        });
    }
}

在我们的情况下,ViewModel类附加到WPF应用程序的主窗口上,在应用程序生命周期内只能有一个ViewModel实例。如果不是这种情况,声明委托的类会更短暂,这会改变什么吗?
我的问题是,我是否正确地处理了回调委托?是否有任何情况可能会通过保留不应该引用的引用而导致内存膨胀?
更一般地说,如何安全和正确地处理匿名委托的处理方式?
4个回答

2

我认为,这样做没问题,而且你不会保留任何不必要的引用。通过在StopTool中清除引用,你将不再持有它们。


我同意,我不认为有理由这样做。这就像将所有本地引用变量设置为null :P - vtortola
不太对。如果StopTool在StartTool的上下文中没有被调用,将这些引用设置为null是正确的。 - Daniel Hilgarth
@fencliff:如果我的回答对您有帮助,请不吝赐教并采纳。 - Daniel Hilgarth
耐心点,小草蜢。既然大家都同意了,我想再等一会儿看看是否有相反的答案。这是你的声望 :) - jevakallio

2

您在删除方法引用方面做得很好。


还有一个问题:

我的问题是,我是否正确地处理了回调委托?

您不需要“处理”方法(或指向方法的指针),只需要处理类。


0

我认为更合适的方式是:

_onComplete = (Action<IResult>)Delegate.Remove(null, _onComplete);

3
按照MSDN的说法,如果第一个参数为null,则Delegate.Remove返回null,因此这样做是否有任何原因?这应该等同于OP所做的操作。请问需要翻译其他内容吗? - Daniel Hilgarth

0
如果您想确保正确处理所有未使用的对象,我建议您使用像CLR Profiler这样的工具,以便您可以完整地查看应用程序如何分配/释放内存。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接