在 lambda 表达式中使用 StringBuilder.Append() 会生成一个空字符串
Using StringBuilder.Append() in a lambda expression is producing an empty string
我正在尝试附加一个字符串列表(> 10),因此为了避免创建大量字符串,我使用 StringBuilder.It 如果有人解释为什么会这样,那就太好了..
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var lst = new List<string>(){"A", "B", "C"};
StringBuilder sb = new StringBuilder();
lst.Select(str => sb.Append(str.ToLower()));
Console.Write("Output: {0}", sb.ToString());
}
}
LINQ 方法 Select
是 lazy。
所以,lst.Select(str => sb.Append(str.ToLower()));
不会改变 sb
要查看效果,您必须遍历结果:
lst.Select(str => sb.Append(str.ToLower())).ToArray();
例如。
来自@thehennyy:
你也可以使用方法List.ForEach
使用ForEach
代替Select
:
static void Main(string[] args)
{
var lst = new List<string>() { "A", "B", "C" };
StringBuilder sb = new StringBuilder();
lst.ForEach(str => sb.Append(str.ToLower()));
Console.Write("Output: {0}", sb.ToString());
Console.ReadKey();
}
或者您可以只使用 LINQ Aggregate
:
var lst = new List<string>() { "A", "B", "C" };
string result = lst.Aggregate((x, y) => x + y).ToLower();
正如其他答案中所指出的,Select
的懒惰是这里的问题所在。我要更广泛地补充一点,在 LINQ 查询中使用副作用(例如附加到 StringBuilder
)通常不是一个好主意。每个选择器、条件等理想情况下都应该没有副作用。
正如 Gilad 指出的那样,您可以在这种情况下使用 string.Concat
- 但您需要使用 Select
将每个值转换为小写,除非您想在事实(我怀疑在某些极端情况下可能会有不同的结果)。
所以要么:
var list = new List<string> { "A", "B", "C" };
string result = string.Concat(list.Select(str => str.ToLower()));
或者,结果可能不同,但创建的字符串更少:
var list = new List<string> { "A", "B", "C" };
string result = string.Concat(list).ToLower();
鉴于 string.Concat
的存在,我不会开始使用 foreach
循环或 List.ForEach
等...没有必要这样做 "manually" 时它内置于框架中。
您可能还想考虑使用 ToLowerInvariant
而不是 ToLower
,具体取决于您要执行的操作。外壳出奇的复杂。
您还可以 .Aggregate
将您的字符串放入构建器中 - 使用每个 .Append
returns 一个新的 StringBuilder
引用这一事实。
StringBuilder result = lst.Aggregate(new StringBuilder(), (sb, str) => sb.Append(str));
这样您就不会滥用 .Select
迭代器,也不会对查询造成副作用。
我正在尝试附加一个字符串列表(> 10),因此为了避免创建大量字符串,我使用 StringBuilder.It 如果有人解释为什么会这样,那就太好了..
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var lst = new List<string>(){"A", "B", "C"};
StringBuilder sb = new StringBuilder();
lst.Select(str => sb.Append(str.ToLower()));
Console.Write("Output: {0}", sb.ToString());
}
}
LINQ 方法 Select
是 lazy。
所以,lst.Select(str => sb.Append(str.ToLower()));
不会改变 sb
要查看效果,您必须遍历结果:
lst.Select(str => sb.Append(str.ToLower())).ToArray();
例如。
来自@thehennyy:
你也可以使用方法List.ForEach
使用ForEach
代替Select
:
static void Main(string[] args)
{
var lst = new List<string>() { "A", "B", "C" };
StringBuilder sb = new StringBuilder();
lst.ForEach(str => sb.Append(str.ToLower()));
Console.Write("Output: {0}", sb.ToString());
Console.ReadKey();
}
或者您可以只使用 LINQ Aggregate
:
var lst = new List<string>() { "A", "B", "C" };
string result = lst.Aggregate((x, y) => x + y).ToLower();
正如其他答案中所指出的,Select
的懒惰是这里的问题所在。我要更广泛地补充一点,在 LINQ 查询中使用副作用(例如附加到 StringBuilder
)通常不是一个好主意。每个选择器、条件等理想情况下都应该没有副作用。
正如 Gilad 指出的那样,您可以在这种情况下使用 string.Concat
- 但您需要使用 Select
将每个值转换为小写,除非您想在事实(我怀疑在某些极端情况下可能会有不同的结果)。
所以要么:
var list = new List<string> { "A", "B", "C" };
string result = string.Concat(list.Select(str => str.ToLower()));
或者,结果可能不同,但创建的字符串更少:
var list = new List<string> { "A", "B", "C" };
string result = string.Concat(list).ToLower();
鉴于 string.Concat
的存在,我不会开始使用 foreach
循环或 List.ForEach
等...没有必要这样做 "manually" 时它内置于框架中。
您可能还想考虑使用 ToLowerInvariant
而不是 ToLower
,具体取决于您要执行的操作。外壳出奇的复杂。
您还可以 .Aggregate
将您的字符串放入构建器中 - 使用每个 .Append
returns 一个新的 StringBuilder
引用这一事实。
StringBuilder result = lst.Aggregate(new StringBuilder(), (sb, str) => sb.Append(str));
这样您就不会滥用 .Select
迭代器,也不会对查询造成副作用。