我只是注意到该方法有11个重载 string.Concat()
public static string Concat(IEnumerable<string> values);
public static string Concat<T>(IEnumerable<T> values);
public static string Concat(object arg0);
public static string Concat(params object[] args);
public static string Concat(params string[] values);
public static string Concat(object arg0, object arg1);
public static string Concat(string str0, string str1);
public static string Concat(object arg0, object arg1, object arg2);
public static string Concat(string str0, string str1, string str2);
public static string Concat(object arg0, object arg1, object arg2, object arg3);
public static string Concat(string str0, string str1, string str2, string str3);
Run Code Online (Sandbox Code Playgroud)
这是什么原因?两者
public static string Concat(params object[] args);
public static string Concat<T>(IEnumerable<T> values);
Run Code Online (Sandbox Code Playgroud)
应该是唯一需要的,因为它们是相同的方便/强大.MSDN没有给出答案,如果你从框架中删除9"重复"重载,没有人会注意到.
Eri*_*ert 16
该实施决策的主要动力是绩效.
正如您所记得的那样,可能只有两个:
public static string Concat(params object[] args);
public static string Concat<T>(IEnumerable<T> values);
Run Code Online (Sandbox Code Playgroud)
如果C#实现了"params可枚举"功能 - 其中可变参数方法可以使用IEnumerable<T>而不是a T[]作为扩展参数 - 那么我们可以只使用一个.或者,我们可能会丢失可枚举的重载并只使用对象数组版本.
假设我们做了后者.你说
string x = Foo();
string y = Bar();
string z = x + y;
Run Code Online (Sandbox Code Playgroud)
会发生什么?在一个只有可变参数的世界中,ToString这只能被编码为
string x = Foo();
string y = Bar();
object[] array = new string[2];
array[0] = x;
array[1] = y;
string z = string.Concat(array);
Run Code Online (Sandbox Code Playgroud)
所以:让我们回顾一下.据推测,调用每个都分配一个字符串.然后我们分配一个短期数组,将引用复制到它,将其传递给可变方法等.需要编写该方法来处理任何大小的数组,处理空数组,等等.
我们不仅在零堆中添加了新的短寿命垃圾; 我们还在活性分析图中创建了两个可能必须遍历的新边.我们可能通过添加压力下降的集合之间的时间量,或通过增加边增加馆藏的成本,或者,最有可能的,收藏成为双方更频繁,更昂贵:一个双重打击.
但等等,还有更多.我们必须考虑被调用Concat方法的实现是什么样的.
对象数组是 - surprise - 一个对象数组,而不是一个字符串数组.那么我们需要做什么?被调用者需要将每个转换为字符串.通过呼吁ToString每个?不,那可能会崩溃.首先检查null,然后调用ToString.
我们传入了字符串,但被调用者不知道.ToString是字符串的标识,但编译器不知道,并且调用是虚拟化的,因此抖动也不能轻易地优化它.所以我们接受了另外几个不必要的几纳秒的检查和间接.更不用说我们需要检查数组是否为null,获取数组的长度,循环遍历数组的每个元素,依此类推.
这些费用非常小,但它们是连接的,它们可能会增加实时花费和内存压力.
许多程序的性能都受到字符串操作和内存压力的影响.我们如何消除或减轻这些成本?
我们可以观察到大多数字符串连接都是两个字符串,因此创建一个专门用于处理这种情况的重载是有意义的:
static string Concat(string, string)
Run Code Online (Sandbox Code Playgroud)
现在我们可以将上面的片段编码为:
string x = Foo();
string y = Bar();
string z = string.Concat(x, y);
Run Code Online (Sandbox Code Playgroud)
现在没有创建数组,因此没有创建额外的垃圾,没有收集压力,参考图中没有新的边缘.在被调用者中,需要检查字符串是否为null,但我们不需要调用ToString实现,因为我们有类型系统来强制操作数已经是字符串,我们不需要检查数组是否为null,我们不需要根据数组长度检查循环变量,依此类推.
因此,我们有一个很好的理由来进行两次重载:一个采用params数组,另一个采用两个字符串.
现在,我们重复该分析,以寻找另一种常见且可能更高效的方案.每个额外的重载都旨在为常见场景提供更有效的替代方案.随着更常见的场景被识别,可以更快,更少资源,有动力产生更多的重载,并修复编译器,以便他们生成利用这些重载的代码.最终结果是大约十几个看似冗余的重载,每个重载都可以调整以获得高性能; 这些涵盖了真实节目中最常见的案例.
如果这个主题让你感兴趣,我写了一篇关于我如何在2006年重新设计字符串连接优化器的一系列文章.
https://ericlippert.com/2013/06/17/string-concatenation-behind-the-scenes-part-one/
| 归档时间: |
|
| 查看次数: |
513 次 |
| 最近记录: |