我知道C#中的字符串有一条规则说:
当我们创建一个string类型的文本字符串时,我们永远不能改变它的值!当为字符串变量设置不同的值时,第一个字符串将保留在内存和变量(这是一种引用类型)中,只获取新字符串的地址。
所以做这样的事情:
string a = "aaa";
a = a.Trim(); // Creates a new string
建议不要。 但是,如果我需要根据用户偏好对字符串执行某些操作,如下所示:
string a = "aaa";
if (doTrim)
a = a.Trim();
if (doSubstring)
a = a.Substring(...);
etc...
如果不在每个动作上创建新字符串,我该怎么办? 我想通过ref将字符串发送到函数,如下所示:
void DoTrim(ref string value)
{
value = value.Trim(); // also creates new string
}
但这也创造了一个新的字符串...... 有人可以告诉我,如果有办法在不浪费记忆力的情况下做到这一点吗?
答案 0 :(得分:11)
你说的是正确的,你正在执行的操作是创建新字符串,而不是改变单个字符串。
您的错误通常是有问题或需要避免的。
如果你的字符串是成千上万的字符,那么确定,复制所有这些只是为了删除一些前导空格,或者在它的末尾添加几个字符(特别是在循环中重复)实际上是一个问题。
如果你的字符串不大,并且你没有在字符串上执行很多(成千上万个)操作,那么你几乎肯定没有一个问题。
现在有一些上下文,通常很少见,做遇到字符串操作问题。可能最常见的有问题的上下文是将一串字符串附加在一起,因为这样做意味着复制每个新添加的所有先前附加的数据。如果您处于这种情况,请考虑使用类似StringBuilder
或string.Concat
的单个调用(重载接受一系列字符串连接)来执行此操作。
其他背景是,例如,处理DNA链的程序。他们经常会捕获数百万个字符串并创建数十万字符长的字符串。因此,使用标准C#字符串操作会导致不必要的复制 lot 。编写此类程序的人最终会创建可以表示另一个字符串的子字符串的对象,而无需复制数据,而是使用偏移量引用现有字符串的基础数据源。
答案 1 :(得分:1)
在这里伸出我的脖子,所以我会在前言中说,在大多数情况下,Servy的回答是正确答案。但是,如果您确实需要较低级别的访问权限和较少的字符串分配,则可以考虑创建一个足够大的字符缓冲区(例如,简单数组)以适合您处理的字符串,并允许您直接操作字符。但是,这有一些重大的挫折。包括你可能必须编写自己的Substring()和Trim()修饰符,并且在很多情况下你的缓冲区可能比输入字符串大,以适应意外的字符串大小。完成操作缓冲区后,可以将字符数组打包为String。由于所有操作都是在单个缓冲区完成的,因此您应该节省大量的分配。
我会认真考虑上述是否值得麻烦,但如果你真的需要性能,这是我能想到的最好的解决方案。
答案 2 :(得分:0)
为什么创建新琴弦会感到不舒服?字符串API以这种方式设计是有原因的。例如,不可变对象是线程安全的(并且它们允许更多功能的编程风格)。
如果用stringbuilders替换简单的字符串代码,那么在多线程场景中你的代码可能更容易出错(例如在Web应用程序中这是很正常的。)
StringBuilders用于连接字符串,插入字符,删除字符等。但是他们也需要不时地重新分配和复制他们的内部字符数组。
当你谈到记忆消耗时,你已经开始micro-optimize your code. Don't.
了顺便说一下:看看LINQ API。每项操作有何作用?老鼠 - 它创建了一个新的枚举器!像foos.Where(bar).Select(baz).FirstOrDefault()
这样的查询当然可以通过创建单个枚举器对象并修改枚举时应用的条件来进行内存优化。 < /讽刺>
答案 3 :(得分:0)
如果不在每个操作上创建新字符串,我该怎么做?
如果您正在处理大字符串或者您在短时间内进行了许多字符串操作,那么您应该只担心这一点。
即便如此,由于创建更多引用而导致的性能损失也很小。 垃圾收集器必须收集所有未使用的字符串变量,但是嘿 - 只有在你做了很多字符串操作时才真正重要。
在您的代码中专注于可读性,而不是尝试优化 性能的地方。
如果你真的必须保持字符串的相同引用,你只需使用StringBuilder。
答案 4 :(得分:-1)
这取决于您的具体用例,但您可能希望使用可用于构建和修改字符串的StringBuilder class进行探索。