令我困扰的是,代码中有很多地方都遵循这种模式:
int amount = 100;
Person person = new Employee();
var em = person as Employee;
if (em != null)
{
em.IncreaseSalary(amount);
}
所以我创建了一个小扩展方法:
public static class Extensions
{
public static void IfAssignable<T>(this object source, Action<T> run) where T : class
{
var target = source as T;
if (target != null)
{
run(target);
}
}
}
现在我能做到:
int amount = 100;
Person person = new Employee();
person.IfAssignable<Employee>(e => e.IncreaseSalary(amount));
amount = 10;
我喜欢这个,因为它更紧凑,而且超出了范围:如果它是null,我也不会使用它。
Resharper然后警告“访问修改后的闭包”。这是因为在相信执行调用之前,“amount”的值可能会发生变化。
我认为实际情况并非如此,因为代码是直接同步执行的。我在这里错过了什么吗?在执行“IncreaseSalary”之前复制到临时值的修复失败了扩展的目的。
编辑:澄清;我(相信我)知道这个警告试图说的是什么。如果我稍后执行代码(意味着异步或订阅事件)并使用从外部作用域隐式传递的值之一,则警告是正确的。
此外,此扩展方法是通用的。我意识到我可以添加任意数量的扩展方法来满足任何数量和类型的参数(改为明确地传递它们),通过方法签名传递它们,并使其工作。扩展方法背后的想法虽然一般适用(除了异步调用,事件订阅,但适用于所有lambda)。
答案 0 :(得分:3)
ReSharper警告你可能修改捕获的变量是正确的,因为它不知道你正在用lambda做什么。在您的情况下,lambda立即执行,因此修改参数没有副作用。
这就是为什么您可以使用[InstantHandle]
code annotation属性指示ReSharper立即处理您的lambda表达式,方法是将其应用于Action<T>
参数:
public static void IfAssignable<T>(this object source,
[InstantHandle] Action<T> run) where T : class
{
var target = source as T;
if (target != null)
{
run(target);
}
}
警告将消失!
您需要将代码注释文件添加到项目中。您可以通过进入ReSharper选项,然后选择代码检查→代码注释来完成此操作。按将默认实施复制到剪贴板,然后将内容粘贴到新文件中。如果更改注释属性的名称空间(默认为JetBrains.Annotations
,则需要将新名称空间添加到“选项”中“代码注释”页面中的设置。
答案 1 :(得分:0)
您可以使用其他'state'参数来避免它:
public static class Extensions
{
public static void IfAssignable<T, TState>(this object source, Action<T, TState> run, TState state) where T : class
{
var target = source as T;
if (target != null)
{
run(target, state);
}
}
}
然后以这种方式使用它:
int amount = 100;
Person person = new Employee();
person.IfAssignable<Employee>((e, amount_) => e.IncreaseSalary(amount_), amount);
amount = 10;
这也是一种避免类型/内存泄漏和其他奇怪错误的方法。