Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit dcd9aae

Browse files
jamesqojkotas
authored andcommitted
Optimize the generic string.Join for length 1 collections (#6463)
* Optimize the generic string.Join for length 1 collections * Keep the call order consistent: MoveNext-Current-ToString * Convert to do..while loop to reduce code duplication
1 parent cfa36dd commit dcd9aae

File tree

1 file changed

+31
-19
lines changed

1 file changed

+31
-19
lines changed

src/mscorlib/src/System/String.cs

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -100,31 +100,52 @@ public static String Join(String separator, params Object[] values) {
100100
}
101101

102102
[ComVisible(false)]
103-
public static String Join<T>(String separator, IEnumerable<T> values) {
103+
public static String Join<T>(String separator, IEnumerable<T> values)
104+
{
104105
if (values == null)
105106
throw new ArgumentNullException("values");
106107
Contract.Ensures(Contract.Result<String>() != null);
107108
Contract.EndContractBlock();
108109

109-
using(IEnumerator<T> en = values.GetEnumerator()) {
110+
using (IEnumerator<T> en = values.GetEnumerator())
111+
{
110112
if (!en.MoveNext())
111-
return String.Empty;
112-
113-
StringBuilder result = StringBuilderCache.Acquire();
113+
return string.Empty;
114+
115+
// We called MoveNext once, so this will be the first item
114116
T currentValue = en.Current;
115117

116-
if (currentValue != null) {
117-
result.Append(currentValue.ToString());
118+
// Call ToString before calling MoveNext again, since
119+
// we want to stay consistent with the below loop
120+
// Everything should be called in the order
121+
// MoveNext-Current-ToString, unless further optimizations
122+
// can be made, to avoid breaking changes
123+
string firstString = currentValue?.ToString();
124+
125+
// If there's only 1 item, simply call ToString on that
126+
if (!en.MoveNext())
127+
{
128+
// We have to handle the case of either currentValue
129+
// or its ToString being null
130+
return firstString ?? string.Empty;
118131
}
119132

120-
while (en.MoveNext()) {
133+
StringBuilder result = StringBuilderCache.Acquire();
134+
135+
result.Append(firstString);
136+
137+
do
138+
{
121139
currentValue = en.Current;
122140

123141
result.Append(separator);
124-
if (currentValue != null) {
142+
if (currentValue != null)
143+
{
125144
result.Append(currentValue.ToString());
126145
}
127-
}
146+
}
147+
while (en.MoveNext());
148+
128149
return StringBuilderCache.GetStringAndRelease(result);
129150
}
130151
}
@@ -161,15 +182,6 @@ public static String Join(String separator, IEnumerable<String> values) {
161182
}
162183
}
163184

164-
165-
#if BIT64
166-
private const int charPtrAlignConst = 3;
167-
private const int alignConst = 7;
168-
#else
169-
private const int charPtrAlignConst = 1;
170-
private const int alignConst = 3;
171-
#endif
172-
173185
internal char FirstChar { get { return m_firstChar; } }
174186

175187
// Joins an array of strings together as one string with a separator between each original string.

0 commit comments

Comments
 (0)