为什么不能在lambda表达式中使用ref或out参数?
我今天遇到了这个错误,并找到了一个解决方案,但我仍然好奇为什么这是一个编译时错误。
CS1628:不能在匿名方法、lambda表达式或查询表达式中使用in ref或out参数'parameter'
这里有一个简单的例子:
private void Foo()
{
int value;
Bar(out value);
}
private void Bar(out int value)
{
value = 3;
int[] array = { 1, 2, 3, 4, 5 };
int newValue = array.Where(a => a == value).First();
}
lambda的外观是改变它们捕获的变量的生命周期。例如,下面的lambda表达式使参数p1比当前方法帧存在的时间更长,因为它的值可以在方法帧不再在堆栈上之后被访问
Func<int> Example(int p1) {
return () => p1;
}
捕获变量的另一个属性是,对变量的更改在lambda表达式之外也是可见的。例如,下面的代码输出42
void Example2(int p1) {
Action del = () => { p1 = 42; };
del();
Console.WriteLine(p1);
}
这两个属性会以以下方式在ref参数面前产生特定的效果:
Ref参数可能有固定的生命周期。考虑将局部变量作为引用参数传递给函数。
lambda中的副作用需要在ref参数本身上可见。在方法内部和调用方内部。
这些属性有些不兼容,也是lambda表达式中不允许使用它们的原因之一。
我再举一个例子。
描述
下面的代码将抛出这个错误。因为lambda表达式(i)=>{…}只在函数test中生效。
static void test(out System.Drawing.Image[] bitmaps)
{
int count = 10;
bitmaps = new System.Drawing.Image[count];
Parallel.For(0, count, (i) =>
{
bitmaps[i] = System.Drawing.Image.FromFile("2.bmp");
});
}
解决方案
如果去掉参数,就可以了。
static void test(System.Drawing.Image[] bitmaps)
{
int count = 10;
bitmaps = new System.Drawing.Image[count];
Parallel.For(0, count, (i) =>
{
bitmaps[i] = System.Drawing.Image.FromFile("2.bmp");
});
}
如果真的需要,不要直接更改lambda表达式中的参数。相反,请使用临时变量。
static void test(out System.Drawing.Image[] bitmaps)
{
int count = 10;
System.Drawing.Image[] bitmapsTemp = new System.Drawing.Image[count];
Parallel.For(0, count, (i) =>
{
bitmapsTemp[i] = System.Drawing.Image.FromFile("2.bmp");
});
bitmaps = bitmapsTemp;
}
因为这是“c# lambda ref”在谷歌上的顶级结果之一;我觉得我有必要对上述答案进行进一步阐述。旧的(c# 2.0)匿名委托语法是有效的,它确实支持更复杂的签名(以及闭包)。Lambda和匿名委托至少在编译器后端共享可感知的实现(如果它们不相同的话)——最重要的是,它们支持闭包。
当我做搜索时,我试图做的是演示语法:
public static ScanOperation<TToken> CreateScanOperation(
PrattTokenDefinition<TNode, TToken, TParser, TSelf> tokenDefinition)
{
var oldScanOperation = tokenDefinition.ScanOperation; // Closures still work.
return delegate(string text, ref int position, ref PositionInformation currentPosition)
{
var token = oldScanOperation(text, ref position, ref currentPosition);
if (token == null)
return null;
if (tokenDefinition.LeftDenotation != null)
token._led = tokenDefinition.LeftDenotation(token);
if (tokenDefinition.NullDenotation != null)
token._nud = tokenDefinition.NullDenotation(token);
token.Identifier = tokenDefinition.Identifier;
token.LeftBindingPower = tokenDefinition.LeftBindingPower;
token.OnInitialize();
return token;
};
}
只要记住Lambdas在程序和数学上更安全(因为前面提到的ref值提升):您可能会打开一个棘手的问题。使用此语法时请仔细考虑。
lambda的外观是改变它们捕获的变量的生命周期。例如,下面的lambda表达式使参数p1比当前方法帧存在的时间更长,因为它的值可以在方法帧不再在堆栈上之后被访问
Func<int> Example(int p1) {
return () => p1;
}
捕获变量的另一个属性是,对变量的更改在lambda表达式之外也是可见的。例如,下面的代码输出42
void Example2(int p1) {
Action del = () => { p1 = 42; };
del();
Console.WriteLine(p1);
}
这两个属性会以以下方式在ref参数面前产生特定的效果:
Ref参数可能有固定的生命周期。考虑将局部变量作为引用参数传递给函数。
lambda中的副作用需要在ref参数本身上可见。在方法内部和调用方内部。
这些属性有些不兼容,也是lambda表达式中不允许使用它们的原因之一。