访问修改后的闭包:ReSharper

Posted

技术标签:

【中文标题】访问修改后的闭包:ReSharper【英文标题】:Access to modified closure: ReSharper 【发布时间】:2015-11-08 12:23:51 【问题描述】:

我创建了一个处理数据库访问的库。我最近添加了事务处理;但是,我遇到了一个小问题。为了概述这一点,我编写了这个示例用于演示目的:

class Program

    static void Main(string[] args)
    
        String data = null;
        DoAction(ref data, () =>
        
            Console.WriteLine(data);
        );
        Console.ReadLine();
    

    private static void DoAction(ref String data, Action action)
    
        if (data == null)
            data = "Initialized Data";
        action();
    

我在“数据”变量的以下代码行中得到“访问修改后的闭包”下划线:

Console.WriteLine(data);

我了解修改 ref 数据变量可能会导致问题(例如,在运行 foreach 循环时)。但是,在以下情况下,我认为不会发生这种情况。

这是另一个版本,其中有一个循环进一步更改变量 - 输出符合预期:

class Program

    static void Main(string[] args)
    
        String data = null;
        for (var i = 0; i < 10; i++)
            DoAction(ref data, () =>
            
                Console.WriteLine(data);
            );
        Console.ReadLine();
    

    private static void DoAction(ref String data, Action action)
    
        if (data == null)
            data = "Initialized Data";
        else
            data += "|";

        action();
    

ReSharper 让我可以创建一个局部变量,但我明确希望使用从 DoAction() 方法创建的字符串。如果我接受 ReSharpers 方法,它实际上会破坏代码。有没有其他方法可以解决这个问题?我想使用这种 Action 方法,但我也不希望 ReSharper 抱怨它(并且可能不会禁用 ReSharpers 检查)。

有什么建议吗?

【问题讨论】:

我还查看了:Access to Modified Closure (2),其中包含指向原始帖子评论中 ReSharper 文档的链接。 【参考方案1】:

我建议首先避免为此使用ref 参数——这对我来说似乎是不必要的复杂。我会将DoAction 重写为:

static string DoAction(string data, Action<string> action)

    data = data == null ? "Initialized Data" : data + "|";
    action(data);
    return data;

那么你可以:

data = DoAction(data, Console.WriteLine);

或者如果你想使用 lambda 表达式:

data = DoAction(data, txt => Console.WriteLine(txt));

如果您之后实际上不需要结果,您可以将 DoAction 设为 void 方法。 (不清楚为什么您需要返回结果DoAction 中执行的委托,但大概这在您更广泛的上下文中更有意义。)

【讨论】:

我明白 - 我也做过同样的事情。但是,对于 Console.WriteLine(string),我会有两次合适的变量。数据和txt。这可能会导致其他团队成员感到困惑。 @AlexanderFuchs:这是一个有点令人困惑的方法——为什么DoAction 负责计算一个新值执行一个动作?如果不了解您真正想要实现的目标,很难说出最佳解决方案是什么,但使用ref 参数对我来说似乎是个坏主意。 对不起,我没有理解 Lambda 之前的解决方案。这看起来不错!我会试试的 这个想法是让 sql 语句与事务一起执行。 DoAction 方法将创建一个事务并将其用于 Lambda 块中的所有查询。不知道这是否有意义。 @AlexanderFuchs:是的,这是有道理的,但是不清楚为什么需要将data 传递到方法中,而不是 在闭包中引用它。 【参考方案2】:

如果您确定该警告不合适,InstantHandleAttribute 记录为:

告诉代码分析引擎参数是否被完全处理 当调用的方法在堆栈上时。如果参数是委托, 表示在执行方法时执行委托。 如果参数是可枚举的,则表示它是可枚举的 方法执行时。

我想这正是你想要的。

您可以从 JetBrains.Annotations 包中获取属性,或者从 ReSharper 选项中复制粘贴。

【讨论】:

我会试试这个 亲爱的上帝,我为此搜索了很长时间,偶然发现了这个解决方案。我有同样的问题,有一个在方法中同步执行然后完成的操作,但 ReSharper 一直告诉我修改后的关闭警告。这是解决方案,无需解决方法。

以上是关于访问修改后的闭包:ReSharper的主要内容,如果未能解决你的问题,请参考以下文章

访问修改后的闭包 - 为啥这是一个建议的修复?

访问修改后的闭包:ReSharper

如何升级到 C# 5.0?访问修改后的闭包

Do.. While.. 使用 Exists 谓词。访问修改后的闭包?

在直接调用委托的情况下如何减轻“访问修改的闭包”

Python 2.x闭包(enclosure)中的变量访问&修改