Пока строки являются неизменяемыми, каждая манипуляция с ними будет вызывать загрузку GC, даже вызовы вставки / удаления StringBuilder.
Я бы вырезал исходную строку по точкам вставки, а затем «заархивировал» ее данными, которые нужно вставить.
После этого вы можете просто объединить строки внутри списка, чтобы получить результирующую строку.
Вот пример кода, который выполняет операции split / zip.
Предполагается, что поля определены как число (позиция, длина, значение).
public class Field
{
public int pos { get; set; }
public int len { get; set; }
public string value { get; set; }
public string tag { get; set; }
}
class Program
{
static void Main(string[] args)
{
var source = "You'r order price [price] and qty [qty].";
var fields = new List<Field>();
fields.Add(new Field()
{
pos = 18,
len = 7,
value = "15.99$",
tag = "price"
});
fields.Add(new Field()
{
pos = 37-3,
len = 5,
value = "7",
tag = "qty"
});
Console.WriteLine(Zip(Split(source, fields), fields));
Console.WriteLine(ReplaceRegex(source, fields));
}
static IEnumerable<string> Split(string source, IEnumerable<Field> fields)
{
var index = 0;
foreach (var field in fields.OrderBy(q => q.pos))
{
yield return source.Substring(index, field.pos - index);
index = field.pos + field.len;
}
yield return source.Substring(index, source.Length - index);
}
static string Zip(IEnumerable<string> splitted, IEnumerable<Field> fields)
{
var items = splitted.Zip(fields, (l, r) => new string[] { l, r.value }).SelectMany(q => q).ToList();
items.Add(splitted.Last());
return string.Concat(items);
}
static string ReplaceRegex(string source, IEnumerable<Field> fields)
{
var fieldsDict = fields.ToDictionary(q => q.tag);
var re = new Regex(@"\[(\w+)\]");
return re.Replace(source, new MatchEvaluator((m) => fieldsDict[m.Groups[1].Value].value));
}
}
Кстати, было бы лучше заменить специальные пользовательские маркеры, такие как [цена], [кол-во], используя регулярное выражение?