OK, first, for the most straightforward optimization. Use a StringBuilder.
The way your code currently works, it creates length - str.Length
intermediate strings. Besides creating lots of unnecessary garbage, you're copying longer and longer strings over and over again to add one character each time.
If you use a StringBuilder instead, you can avoid most of those copies.
You could try something like...
using System.Text;
...
private string ExpandString(string str, int length)
{
if (length <= str.Length) return str.Substring(0, length);
var result = new StringBuilder(str);
for (var i = str.Length; i < length; i++)
{
result.Append(str[i % str.Length]);
}
return result.ToString();
}
(I don't particularly like the StringBuilder starting with a copy of str
already in it. I'm literally just replacing the String.)
But you're also copying one character at a time. While this isn't as big an issue as the concatenation, you can do better by inserting as many whole copies of str
as you can.
private string ExpandString(string str, int length)
{
// BTW, you already know how big the result should be, so just
// tell the StringBuilder its capacity to prevent unneeded resizes.
// Side benefit: if the result is too long, you'll find out fairly quickly.
var result = new StringBuilder(length, length);
var wholeCopies = length / str.Length;
for (var i = 0; i < wholeCopies; ++i)
{
result.Append(str);
}
// now append the last chunk, a possibly-zero-length prefix of `str`
result.Append(str, 0, length % str.Length);
return result.ToString();
}
Note that the short-string optimization has been removed. You can add it back in if you really really want to micro-optimize, but at this point, it's not gaining you nearly as much as it was.
return string.IsNullOrEmpty(str) ? "".PadLeft(length,' ') : string.Join("", Enumerable.Repeat(str, (int)Math.Ceiling((double)length / (double)str.Length)).ToArray()).Substring(0, length);
\$\endgroup\$