Как смягчить «Доступ к измененному закрытию» в случаях, когда делегат вызывается напрямую

2

Насколько я понимаю, предупреждение "Доступ к измененному закрытию" предназначено для предупреждения о доступе к локальным переменным из делегата, когда делегат может быть сохранен и вызван позже или вызван в другом потоке, так что локальная переменная фактически недоступна в время фактического выполнения кода. Это разумно, конечно.

Но что, если я создаю делегата, который, как я знаю, будет немедленно вызван в том же потоке? Предупреждение тогда не нужно. Например, предупреждение генерируется в коде:

delegate void Consume();

private void ConsumeConsume(Consume c)
{
    c();
}

public int Hello()
{
    int a = 0;

    ConsumeConsume(() => { a += 9; });

    a = 1;

    return a;
}

Здесь не может быть никаких проблем, так как ConsumeConsume всегда вызывает функцию немедленно. Есть ли способ обойти это? Есть ли способ аннотировать функцию ConsumeConsume чтобы указать ReSharper, что делегат будет вызван немедленно?

Интересно, когда я заменяю ConsumeConsume(() => { a += 9; }); линия с:

new List<int>(new[] {1}).ForEach(i => { a += 9; });

который делает то же самое, предупреждение не генерируется. Это встроенное исключение для ReSharper или я могу сделать что-то подобное, чтобы указать, что делегат вызывается немедленно?

Я знаю, что могу отключить эти предупреждения, но это нежелательный результат.

Теги:
closures
resharper

2 ответа

4
Лучший ответ

Установите пакет JetBrains.Annotations с NuGet: https://www.nuget.org/packages/JetBrains.Annotations

Отметьте переданный в InstantHandle атрибут InstantHandle.

private void ConsumeConsume([InstantHandle] Consume c)
{
    c();
}

Из описания InstantHandle:

Сообщает механизму анализа кода, полностью ли обработан параметр, когда вызываемый метод находится в стеке. Если параметр является делегатом, указывает, что делегат выполняется во время выполнения метода. Если параметр является перечисляемым, указывает, что он перечисляется во время выполнения метода.

Источник: https://www.jetbrains.com/help/resharper/Reference__Code_Annotation_Attributes.html

Если вы не хотите добавлять весь пакет в свой проект, достаточно просто добавить атрибут самостоятельно, хотя, на мой взгляд, это хакерство.

namespace JetBrains.Annotations
{
    [AttributeUsage(AttributeTargets.Parameter)]
    public class InstantHandleAttribute : Attribute { }
}
  • 0
    Отлично! Спасибо.
0

Да, это только предупреждение, вы можете смягчить его следующим

// ReSharper disable once AccessToModifiedClosure
ConsumeConsume(() => { a += 9; });

Или на уровне файла

// ReSharper disable AccessToModifiedClosure
namespace blah 
{

Или вы можете настроить серьезность проверки, однако я бы не стал отключать это в общем смысле, так как это может выявить проблемы.

Обновить

Извините, мой вопрос не был достаточно ясен, я думаю. Наличие команд отключения предупреждений везде не является приемлемым решением для меня, я бы предпочел, чтобы ReSharper нашел способ это выяснить. Пример ForEach демонстрирует, что есть, по крайней мере, некоторые существующие исключения.

Вы можете отключить его, но, к сожалению, его никак не понять.

  • 0
    Извините, мой вопрос не был достаточно ясен, я думаю. Наличие команд отключения предупреждений везде не является приемлемым решением для меня, я бы предпочел, чтобы ReSharper нашел способ это выяснить. Пример ForEach демонстрирует, что есть, по крайней мере, некоторые существующие исключения.
  • 0
    Я думаю, что ваше обновление должно быть комментарием (вы отвечаете на мой комментарий). Отключить это то же самое (на самом деле, намного хуже), чем отключить. Опять же, тот факт, что ReSharper каким-то образом делает исключение для ForEach указывает на то, что кто-то где-то задумался или принял решение этой проблемы, не включающее локальное отключение.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню