不能在 lambda 表达式中使用 ref 或 out 参数
Posted
技术标签:
【中文标题】不能在 lambda 表达式中使用 ref 或 out 参数【英文标题】:Cannot use ref or out parameter in lambda expressions 【发布时间】:2010-11-24 20:10:22 【问题描述】:为什么?
我今天遇到了这个错误并找到了解决方法,但我仍然很好奇为什么这是一个编译时错误。
CS1628: 不能在匿名方法、lambda 表达式或查询表达式中使用 in ref 或 out 参数“参数”
这是一个简单的例子:
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();
【问题讨论】:
这是关于迭代器的,但这篇文章中的大部分推理(同样由 Eric Lippert 撰写,毕竟他是语言设计团队的成员)适用于 lambda:blogs.msdn.com/ericlippert/archive/2009/07/13/…> 请问您找到的解决方法是什么? 您可以声明一个局部普通变量并使用它,然后将结果分配给 value... 添加一个 var tempValue = value;然后使用 tempValue。 文章@JoelCoehoorn的comment指的现在可以找到here 【参考方案1】: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 参数可能具有固定的生命周期。考虑将局部变量作为 ref 参数传递给函数。 lambda 中的副作用需要在 ref 参数本身上可见。在方法中和调用者中。这些属性有些不兼容,也是 lambda 表达式中不允许使用它们的原因之一。
【讨论】:
如果你还想使用它,那么你可以创建一个临时变量并在 lamda 中使用它。类似int tempVariable = refVariable; int newValue = array.Where(a => a == tempVariable).First();
【参考方案2】:
在后台,匿名方法是通过提升捕获的变量(这是您的问题主体的全部内容)并将它们存储为编译器生成的类的字段来实现的。无法将ref
或out
参数存储为字段。 Eric Lippert 在a blog entry 中讨论了它。请注意,捕获的变量和 lambda 参数之间存在差异。您可以拥有如下“形式参数”,因为它们不是捕获的变量:
delegate void TestDelegate (out int x);
static void Main(string[] args)
TestDelegate testDel = (out int x) => x = 10; ;
int p;
testDel(out p);
Console.WriteLine(p);
【讨论】:
【参考方案3】:你可以,但你必须明确定义所有类型
(a, b, c, ref d) => ...
但是无效
(int a, int b, int c, ref int d) => ...
有效
【讨论】:
确实如此;问题是你为什么不能;答案是可以的。 它没有;问题是为什么您不能在 lambda 中引用已定义ref
或 out
的现有变量。如果您阅读示例代码,则很清楚(再次尝试再次阅读)。接受的答案清楚地解释了原因。您的答案是关于使用 ref
或 out
parameter 到 lambda。完全不回答问题并谈论其他事情
@edc65 是对的......这与问题的主题无关,这是关于lamba表达式的内容(在右边),而不是它的参数列表(在左边)。奇怪的是,这得到了 26 票。
但我还是不明白为什么它被设计成这样。为什么我必须明确定义所有类型?语义上我不需要。我失去了什么吗?
@joe 我认为原因是语法上的。【参考方案4】:
因为这是 Google 上“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;
;
请记住,Lambda 在程序和数学上更安全(因为前面提到的 ref 值提升):您可能会打开一罐蠕虫。使用此语法时请仔细考虑。
【讨论】:
我认为您误解了这个问题。问题是为什么 lambda 不能在其容器方法中访问 ref/out 变量,而不是为什么 lambda 本身 不能包含 ref/out 变量。 AFAIK 后者没有充分的理由。今天我写了一个 lambda(a, b, c, ref d) => ...
和 ref
是红色下划线的错误消息“参数'4'必须用'ref'关键字声明”。捂脸!附言什么是“参考价值提升”?
@Qwertie 我得到这个可以完全参数化,意思是,包括 a、b、c 和 d 上的类型,它可以工作。请参阅 BenAdams 的回答(尽管他也误解了最初的问题)。
@Qwertie 我想我只删除了该点的一半 - 我认为最初的观点是将 ref 参数放入闭包中可能有风险,但我随后一定意识到这并没有发生在我给出的例子(我也不知道它是否会编译)。
这与实际提出的问题无关......请参阅 Ben Adams 的已接受答案和答案下的 cmets,他同样误解了这个问题。【参考方案5】:
也许这个?
private void Foo()
int value;
Bar(out value);
private void Bar(out int value)
value = 3;
int[] array = 1, 2, 3, 4, 5 ;
var val = value;
int newValue = array.Where(a => a == val).First();
【讨论】:
以上是关于不能在 lambda 表达式中使用 ref 或 out 参数的主要内容,如果未能解决你的问题,请参考以下文章