<SystemSources Condition="'$(FeatureClassicCominterop)' == 'true'" Include="$(BclSourcesRoot)\System\OleAutBinder.cs" />
</ItemGroup>
<ItemGroup>
- <SystemSources Condition="'$(FeatureCoreclr)' == 'true'" Include="$(BclSourcesRoot)\Internal\Runtime\Augments\EnvironmentAugments.cs" />
+ <InternalSources Condition="'$(FeatureCoreclr)' == 'true'" Include="$(BclSourcesRoot)\Internal\Runtime\Augments\EnvironmentAugments.cs" />
</ItemGroup>
<ItemGroup>
<ReflectionSources Include="$(BclSourcesRoot)\System\Reflection\__Filters.cs" />
<MscorlibSources Include="@(IdentitySources)"/>
<MscorlibSources Include="@(VersioningSources)"/>
<MscorlibSources Include="@(DesignerServicesSources)"/>
+ <MscorlibSources Include="@(InternalSources)"/>
<MscorlibSources Include="$(BclSourcesRoot)\GlobalSuppressions.cs"/>
</ItemGroup>
</Project>
}
[System.Security.SecuritySafeCritical]
- public static String Concat(params Object[] args) {
- if (args==null) {
+ public static string Concat(params object[] args)
+ {
+ if (args == null)
+ {
throw new ArgumentNullException("args");
}
Contract.Ensures(Contract.Result<String>() != null);
Contract.EndContractBlock();
-
- String[] sArgs = new String[args.Length];
- int totalLength=0;
+
+ // We need to get an intermediary string array
+ // to fill with each of the args' ToString(),
+ // and then just concat that in one operation.
+
+ // This way we avoid any intermediary string representations,
+ // or buffer resizing if we use StringBuilder (although the
+ // latter case is partially alleviated due to StringBuilder's
+ // linked-list style implementation)
+
+ var strings = new string[args.Length];
- for (int i=0; i<args.Length; i++) {
+ int totalLength = 0;
+
+ for (int i = 0; i < args.Length; i++)
+ {
object value = args[i];
- sArgs[i] = ((value==null)?(String.Empty):(value.ToString()));
- if (sArgs[i] == null) sArgs[i] = String.Empty; // value.ToString() above could have returned null
- totalLength += sArgs[i].Length;
- // check for overflow
- if (totalLength < 0) {
+
+ string toString = value?.ToString() ?? string.Empty; // We need to handle both the cases when value or value.ToString() is null
+ strings[i] = toString;
+
+ totalLength += toString.Length;
+
+ if (totalLength < 0) // Check for a positive overflow
+ {
throw new OutOfMemoryException();
}
}
+ // If all of the ToStrings are null/empty, just return string.Empty
+ if (totalLength == 0)
+ {
+ return string.Empty;
+ }
+
string result = FastAllocateString(totalLength);
- int currPos = 0;
- for (int i = 0; i < sArgs.Length; i++)
+ int position = 0; // How many characters we've copied so far
+
+ for (int i = 0; i < strings.Length; i++)
{
- Contract.Assert(currPos <= totalLength - sArgs[i].Length, "[String.Concat](currPos <= totalLength - sArgs[i].Length)");
- FillStringChecked(result, currPos, sArgs[i]);
- currPos += sArgs[i].Length;
+ string s = strings[i];
+
+ Contract.Assert(s != null);
+ Contract.Assert(position <= totalLength - s.Length, "We didn't allocate enough space for the result string!");
+
+ FillStringChecked(result, position, s);
+ position += s.Length;
}
return result;