Неявно зафиксированные закрытия, предупреждение 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
Ответы
Ответ 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 не сообщал об этом.