c# - 在内存方面,将一个长的非动态字符串存储为单个字符串对象还是让程序从重复部分构建它更好?

标签 c# string memory-management

这是一个有点奇怪的问题,更多的是我需要的任何实验,但我仍然对答案很好奇:如果我有一个我提前知道的字符串永远不会改变但(大部分)由重复部分组成,将字符串作为单个字符串对象,在需要时调用并完成它会更好 - 或者我应该将字符串分成更小的字符串代表重复部分并在需要时连接它们?

让我举个例子:假设我们有一个天真的程序员想要创建一个正则表达式来验证 IP 地址(换句话说,我知道这个正则表达式不会按预期工作,但它有助于说明我的想法意思是重复的部分,并为示例的第二部分节省了一些输入)。所以他写了这个函数:

 private bool isValidIP(string ip)
 {
   Regex checkIP = new Regex("\\d\\d?\\d?\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\.\\d\\d?\\d?");
   return checkIP.IsMatch(ip);
 }

现在我们的年轻程序员注意到他有“\d”、“\d?”和“\”。只是重复了几次。这给了他一个想法,他既可以节省一些存储空间,又可以帮助提醒自己这对以后意味着什么。于是他重做了函数:

 private bool isValidIP(string ip)
 {
   string escape = "\\";
   string digi = "d";
   string digit = escape + digi;
   string possibleDigit = digit + '?';
   string IpByte = digit + possibleDigit + possibleDigit;
   string period = escape + '.';
   Regex checkIP = new Regex(IpByte + period + IpByte + period + IpByte + period + IpByte);
   return checkIP.IsMatch(ip);
 }

第一种方法很简单。它只是在程序的指令中存储了 38 个字符,每次调用该函数时都会将它们读入内存。 第二种方法将(我怀疑)两个长度为 1 的字符串和两个字符存储到程序的指令中,以及将这四个字符串连接成不同顺序的所有调用。这会在调用程序时在内存中创建至少 8 个字符串(六个命名字符串,正则表达式前四部分的临时字符串,然后是从前一个字符串创建的最终字符串 + 正则表达式的三个字符串)。第二种方法也恰好有助于解释正则表达式在寻找什么——尽管不是最终正则表达式的样子。它还可以帮助重构,比如说,如果我们假设的程序员意识到他当前的正则表达式在 IP 地址中允许的不仅仅是 0-255,并且可以更改构成部分而不必找到需要的每一个项目固定。

同样,哪种方法更好?它会像程序大小与内存使用之间的权衡一样简单吗? 当然,对于像这样简单的东西,权衡最多可以忽略不计,但是更大、更复杂的字符串呢?

哦,是的,一个更好的 IP 地址正则表达式是:

 ^(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)(\\.(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)){3}$

不会像示例一样有效,对吗?

最佳答案

到目前为止,第一个是更好的选择。这就是为什么

  1. 更清晰。

  2. 它更便宜。任何时候声明一个新对象都是一个“昂贵”的过程。您必须在堆上为它腾出空间(至少对于字符串而言)。是的,理论上你可以节省一个字节左右,但是你花费了更多的时间(可能我还没有测试过)为每个字符串分配空间,额外的内存指令等。更不用说这个事实了请记住,您还必须考虑 GC 的使用。您不断分配字符串,最终您将不得不应对它占用的进程滴答声。您真的想进行优化,我可以很容易地看出这段代码没有达到应有的效率。一件事没有常量,这意味着您可能创建了比您需要的更多的对象,而不是让编译器针对不需要更改的字符串进行优化。这让我想到,作为审查此代码的人,我需要更仔细地查看将要发生的事情,并找出是否有问题。

  3. 更清楚(是的,我又说了一遍)。你想做一个学术追求,看看你能做到多高效。这很酷。我明白了。我自己做。好有趣。我从不让它进入生产代码。我不在乎失去一个滴答声,我在乎在生产中有一个错误,我在乎其他程序员是否能理解我的代码的作用。阅读别人的代码已经够难了,我不想增加他们必须尝试弄清楚我放入了哪个微优化以及如果他们“轻推”错误代码段会发生什么的额外任务。

  4. 您说到了另一点。如果原始正则表达式错误怎么办。 Google 会告诉你这个问题已经解决了。您可以用谷歌搜索另一个正确且已经过测试的正则表达式。你不能用谷歌搜索“我的代码有什么问题”。您当然可以将它发布到 SO 上,但这意味着必须有其他人参与并查看它。

下面是让第一个例子轻松赢得赛马的方法:

 Regex checkIP = new Regex(
   "\\d\\d?\\d?\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\.\\d\\d?\\d?");

 private bool isValidIP(string ip)
 {
   return checkIP.IsMatch(ip);
 }

声明一次,重复使用。如果您花时间动态地重新创建正则表达式以保存一些,请不要这样做。从技术上讲,您可以这样做并且仍然只创建一次对象,但这比说将它移动到类级别变量要多得多。

关于c# - 在内存方面,将一个长的非动态字符串存储为单个字符串对象还是让程序从重复部分构建它更好?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10019533/

相关文章:

python - Pandas DataFrame 子字符串匹配不起作用

c - 获取字符串直到第一个数字

ios - NSZombies 在未启用时崩溃,在启用时工作

objective-c - View 和其他对象的泄漏

c# - Unity 从资源中加载文本

c# - Webdriver 如何等到元素在 webdriver C# 中可点击

c# - 解耦依赖于构造函数接受参数的另一个类的类

c# - 如何从另一个窗体禁用一个窗体控件?

string - 环绕字符串的唯一子字符串

c++ - 如何在 C++ 中正确释放二维数组的内存?