Подтвердить что ты не робот

Неявно зафиксированные закрытия, предупреждение ReSharper

Я обычно знаю, что означает "неявно зафиксированное закрытие", однако сегодня я столкнулся со следующей ситуацией:

public static void Foo (Bar bar, Action<int> a, Action<int> b, int c)
{
    bar.RegisterHandler(x => a(c)); // Implicitly captured closure: b
    bar.RegisterHandler(x => b(c)); // Implicitly captured closure: a
}

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

Изменить: ReSharper 8.0.1

4b9b3361

Ответ 1

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

Это означает, что время жизни первого делегата удерживает объект замыкания живым и содержит ссылку на объект b в дополнение к a, внутренне и наоборот.

Теперь в вашем случае это не проблема, так как Action - это не то, что особенно интенсивно для памяти, поэтому сохранение его дольше дольше не является проблемой.

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

Ответ 2

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

Ответ 3

И предупреждение, я сошел с ума по этому поводу:

List<List<string>> allowed = AllowedSCACSwaps;
foreach (List<string> c in allowed.Where(c => c.Contains(scac)))
{
    csc = openCycles.FirstOrDefault(icsc => (icsc.CustomerCode == customerCode) && c.Contains(icsc.SCAC));
    if (null != csc)
    {
        return csc;
    }
}

говоря "неявное закрытие для customerCode"

string cc = customerCode.ToUpperInvariant();
string sc = scac.ToUpperInvariant();    List<List<string>> allowed = AllowedSCACSwaps;
    foreach (List<string> c in allowed.Where(c => c.Contains(sc)))
    {
        csc = openCycles.FirstOrDefault(icsc => (icsc.CustomerCode == cc) && c.Contains(icsc.SCAC));
        if (null != csc)
        {
            return csc;
        }
    }

Прекрасно.

Почему я сошел с ума?

scac и customerCode - это обе строки, переданные в метод. Но даже когда я заменил customerCode на cc, я продолжал получать то же предупреждение.

Закрытие было фактически на scac, но Resharper не сообщал об этом.