c# - 递归地将嵌套的 lambda 表达式构建为变量,在特定情况下奇怪的 stackoverflow。意外行为
问题描述
首先,一些示例代码。
我对我的问题进行了以下抽象,您可以将其放入 linqpad。
Func<int, int> recursiveSomethingIntegers(int b){
if(b == 1){
return x => x * x;
}
if(b == 2){
return x => x * x + 1;
}
var intArray = new int[]{1, 2};
Func<int, int> lExpression = null;
Func<int, int> someOtherExpression = null;
if(b == 3){
foreach(var a in intArray){
if(lExpression == null){
Console.WriteLine("Called only once");
lExpression = recursiveSomethingIntegers(a);
continue;
}
else{
Console.WriteLine("Also called only once");
// This statement leads to the error
lExpression = x => lExpression(a) + 2;
// This one, which has the same initialization doesn't.
someOtherExpression = x => lExpression(a) + 2;
// Comment out either one of the above
}
}
}
return someOtherExpression ?? lExpression;
}
var someExpression = recursiveSomethingIntegers(3);
Console.WriteLine(someExpression(3));
如您所见,代码完全没有意义。尽管如此,我相信重用变量所产生的问题并不是有意的设计。
在 Visual Studio 中调试它时,它给了我一个“访问冲突”错误,而不是异常,至少在我的真实代码中是这样。
如果我正常运行它,它会给出一个 StackOverflowException 。
当 lambda 正在执行时,Console.WriteLine(someExpression(3));
它会卡住lExpression = x => lExpression(a) + 2;
并不断添加到堆栈中,显然会导致堆栈溢出。同时使用另一个变量很好。
如果您使用普通变量而不是 lambda 表达式来执行此操作,那很好。
在我的用例中,我需要再次使用相同的变量名,因为我的 foreach 循环应该能够运行任意数量的循环。
我的问题是这是否真的是无意的 C#/编译器/运行时行为?因为对我来说,这似乎违反了语言的设计。
编辑:我正在更多地研究调试器,我认为在初始化中lExpression = x => lExpression(a) + 2;
改进了 BOTHlExpression
作为第一个lExpression
.
这就像x = x + 1
将 BOTH 重新定义x
为 的结果x + 1
,这没有任何意义。
EDIT2:解决这个问题的方法:而不是
lExpression = x => lExpression(a) + 2;
做:
var clonedExpression = lExpression.Clone() as Func<int, int>;
lExpression = x => clonedExpression(a) + 2;
我不完全理解为什么这会起作用。
另一个示例,如果您希望使用 Visual Studio 项目进行尝试:
using System;
namespace ConsoleApp1
{
class Program
{
public Program()
{
var someExpression = recursiveSomethingIntegers(3);
System.Diagnostics.Debug.WriteLine(someExpression(3));
}
static void Main(string[] args)
{
var p = new Program();
}
Func<int, int> recursiveSomethingIntegers(int b)
{
if (b == 1)
{
return x => x * x;
}
if (b == 2)
{
return x => x * x + 1;
}
var intArray = new int[] { 1, 2 };
Func<int, int> lExpression = null;
Func<int, int> someOtherExpression = null;
if (b == 3)
{
foreach (var a in intArray)
{
if (lExpression == null)
{
System.Diagnostics.Debug.WriteLine("Called only once");
lExpression = recursiveSomethingIntegers(a);
continue;
}
else
{
System.Diagnostics.Debug.WriteLine("Also called only once");
// This statement leads to the error
lExpression = x => lExpression(a) + 2;
// This one, which has the same initialization, doesn't.
someOtherExpression = x => lExpression(a) + 2;
// Comment out either one of the above, don't leave both uncommented
}
}
}
return someOtherExpression ?? lExpression;
}
}
}
解决方案
推荐阅读
- awk - 将类似 GenBank 的多行记录转换为新的文件格式(fasta 格式)
- javascript - JavaScript 异步性和运行时
- html - 为什么通过beautifulsoup导入的html和实际的html不一样?
- javascript - 在 Codepen 中使用 React 但没有得到任何东西
- c++ - 如何序列化/反序列化 std::map
C ++中的到/从json - complexity-theory - 计算复杂性:对大多数决策问题无法计算的证明中的矛盾感到困惑
- dax - 使用 dax 转换为正确的大小写
- loops - (AHK) 如何点击 X 次,等待,然后重复
- bash - Bash 脚本不应该执行一次
- css - 如何将最大宽度大于 100% 的 iframe 居中对齐以裁剪一些 iframe 宽度?