局部函数和共享变量的奇怪行为
Weird behavior with local functions and shared variables
我正在为预算生成一个 Excel 文件,但本地函数有一个奇怪的行为。问题似乎出在共享变量 rowIndex
上。由于某种原因,总公式会覆盖每个部分第一个单元格的内容,但 "Total active" 标签不会覆盖帐户名称。有人可以解释是什么导致了这种奇怪的行为吗?
代码:
private void Export()
{
var workbook = new Aspose.Cells.Workbook();
var worksheet = workbook.Worksheets[0];
int rowIndex = 0;
var number = 0;
var data = new List<dynamic>
{
new { Name = $"Account {++number}", Amount = 50, IsIncome = true, Active = true },
new { Name = $"Account {++number}", Amount = 25, IsIncome = true, Active = false },
new { Name = $"Account {++number}", Amount = 75, IsIncome = false, Active = true },
new { Name = $"Account {++number}", Amount = 100, IsIncome = false, Active = false },
new { Name = $"Account {++number}", Amount = 60, IsIncome = true, Active = false },
new { Name = $"Account {++number}", Amount = 90, IsIncome = false, Active = true },
new { Name = $"Account {++number}", Amount = 40, IsIncome = true, Active = true },
new { Name = $"Account {++number}", Amount = 20, IsIncome = false, Active = false }
};
WriteExportSection("INCOME", data.Where(i => i.IsIncome).ToList());
WriteExportSection("EXPENSES", data.Where(i => !i.IsIncome).ToList());
workbook.Save("budget.xlsx");
IEnumerable<int> WriteRowValues(IList<dynamic> items)
{
foreach(var item in items)
{
worksheet.Cells[rowIndex, 0].Value = item.Name;
worksheet.Cells[rowIndex, 1].Value = item.Amount;
worksheet.Cells[rowIndex, 2].Value = item.Active;
if (item.Active)
{
yield return rowIndex;
}
rowIndex++;
}
}
void WriteExportSection(string name, IList<dynamic> items)
{
worksheet.Cells[rowIndex, 0].Value = name;
rowIndex++;
worksheet.Cells[rowIndex, 0].Value = "Account";
worksheet.Cells[rowIndex, 1].Value = "Amount";
worksheet.Cells[rowIndex, 2].Value = "Is active";
rowIndex++;
var activeRows = WriteRowValues(items);
rowIndex++;
worksheet.Cells[rowIndex, 0].Value = "Total active";
worksheet.Cells[rowIndex, 1].Formula = "SUM(" + string.Join(",", activeRows.Select(r => worksheet.Cells[r, 1].Name)) + ")";
rowIndex++;
}
}
这是因为WriteRowValues
正在使用延迟执行。因为它使用 yield
,所以它不会执行,直到您开始使用表达式迭代它:
activeRows.Select(r => worksheet.Cells[r, 1].Name))
因此,在您已经开始输出总行数之前,rowIndex
变量不会递增。这些行输出总数:
worksheet.Cells[rowIndex, 0].Value = "Total active";
worksheet.Cells[rowIndex, 1].Formula = "SUM(" + string.Join(",", activeRows.Select(r => worksheet.Cells[r, 1].Name)) + ")";
仍具有 rowIndex
的原始值,未增加。也就是数据的"first row"。
最简单的解决方法是立即强制执行:
var activeRows = WriteRowValues(items).ToList();
更合适的解决方法可能是完全放弃延迟执行,因为您似乎没有利用它,而且它可能会在以后引起问题(比如如果您最终迭代它两次):
List<int> WriteRowValues(IList<dynamic> items)
{
var activeIndexes = new List<int>();
foreach(var item in items)
{
worksheet.Cells[rowIndex, 0].Value = item.Name;
worksheet.Cells[rowIndex, 1].Value = item.Amount;
worksheet.Cells[rowIndex, 2].Value = item.Active;
if (item.Active)
{
activeIndexes.Add(rowIndex);
}
rowIndex++;
}
return activeIndexes;
}
我正在为预算生成一个 Excel 文件,但本地函数有一个奇怪的行为。问题似乎出在共享变量 rowIndex
上。由于某种原因,总公式会覆盖每个部分第一个单元格的内容,但 "Total active" 标签不会覆盖帐户名称。有人可以解释是什么导致了这种奇怪的行为吗?
代码:
private void Export()
{
var workbook = new Aspose.Cells.Workbook();
var worksheet = workbook.Worksheets[0];
int rowIndex = 0;
var number = 0;
var data = new List<dynamic>
{
new { Name = $"Account {++number}", Amount = 50, IsIncome = true, Active = true },
new { Name = $"Account {++number}", Amount = 25, IsIncome = true, Active = false },
new { Name = $"Account {++number}", Amount = 75, IsIncome = false, Active = true },
new { Name = $"Account {++number}", Amount = 100, IsIncome = false, Active = false },
new { Name = $"Account {++number}", Amount = 60, IsIncome = true, Active = false },
new { Name = $"Account {++number}", Amount = 90, IsIncome = false, Active = true },
new { Name = $"Account {++number}", Amount = 40, IsIncome = true, Active = true },
new { Name = $"Account {++number}", Amount = 20, IsIncome = false, Active = false }
};
WriteExportSection("INCOME", data.Where(i => i.IsIncome).ToList());
WriteExportSection("EXPENSES", data.Where(i => !i.IsIncome).ToList());
workbook.Save("budget.xlsx");
IEnumerable<int> WriteRowValues(IList<dynamic> items)
{
foreach(var item in items)
{
worksheet.Cells[rowIndex, 0].Value = item.Name;
worksheet.Cells[rowIndex, 1].Value = item.Amount;
worksheet.Cells[rowIndex, 2].Value = item.Active;
if (item.Active)
{
yield return rowIndex;
}
rowIndex++;
}
}
void WriteExportSection(string name, IList<dynamic> items)
{
worksheet.Cells[rowIndex, 0].Value = name;
rowIndex++;
worksheet.Cells[rowIndex, 0].Value = "Account";
worksheet.Cells[rowIndex, 1].Value = "Amount";
worksheet.Cells[rowIndex, 2].Value = "Is active";
rowIndex++;
var activeRows = WriteRowValues(items);
rowIndex++;
worksheet.Cells[rowIndex, 0].Value = "Total active";
worksheet.Cells[rowIndex, 1].Formula = "SUM(" + string.Join(",", activeRows.Select(r => worksheet.Cells[r, 1].Name)) + ")";
rowIndex++;
}
}
这是因为WriteRowValues
正在使用延迟执行。因为它使用 yield
,所以它不会执行,直到您开始使用表达式迭代它:
activeRows.Select(r => worksheet.Cells[r, 1].Name))
因此,在您已经开始输出总行数之前,rowIndex
变量不会递增。这些行输出总数:
worksheet.Cells[rowIndex, 0].Value = "Total active";
worksheet.Cells[rowIndex, 1].Formula = "SUM(" + string.Join(",", activeRows.Select(r => worksheet.Cells[r, 1].Name)) + ")";
仍具有 rowIndex
的原始值,未增加。也就是数据的"first row"。
最简单的解决方法是立即强制执行:
var activeRows = WriteRowValues(items).ToList();
更合适的解决方法可能是完全放弃延迟执行,因为您似乎没有利用它,而且它可能会在以后引起问题(比如如果您最终迭代它两次):
List<int> WriteRowValues(IList<dynamic> items)
{
var activeIndexes = new List<int>();
foreach(var item in items)
{
worksheet.Cells[rowIndex, 0].Value = item.Name;
worksheet.Cells[rowIndex, 1].Value = item.Amount;
worksheet.Cells[rowIndex, 2].Value = item.Active;
if (item.Active)
{
activeIndexes.Add(rowIndex);
}
rowIndex++;
}
return activeIndexes;
}