Итак, сначала шаг: Я придумал самое простое и рабочее решение:
Код: Выделить всё
public static string ToPartialLowerCase(this string input, int startIndex, int length)
{
return string.Concat(
input[..startIndex],
input[startIndex..(startIndex + length)].ToLowerInvariant(),
input[(startIndex + length)..]);
}
Трижды операция подстроки и один раз операция объединения. Верно?
Следующий шаг: я хочу быть уверен, что возвращаю ту же строку, если не требуется ввод строчных букв. Итак, я написал тест:
Код: Выделить всё
object.ReferenceEquals(
"aaaa",
"aaaa".ToPartialLowerCase(0, 4))
.Should().BeTrue();
Ну, к моему удивлению, он стал зеленым.
Хорошо, я читал об интернировании строк, но ни изучение исходного кода string.Concat, ни просмотр урезанного кода C# на сайте Sharplab.io не дали мне никаких намеков на то, как это возможно.
Хорошо, я подумал: «Может быть, интернирование строк прозрачно и выполняется JIT или чем-то в этом роде».
Но я наверняка увижу как минимум три выделения из операций с подстроками в тесте с памятью диагностика, да?
Код: Выделить всё
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkRunner.Run();
[MemoryDiagnoser]
public class Tests
{
[Benchmark]
public string NoChange()
{
return "test".ToPartialLowerCase(0, 4);
}
[Benchmark]
public string WithChange()
{
return "TEST".ToPartialLowerCase(0, 4);
}
}
public static class StringExtensions
{
public static string ToPartialLowerCase(this string input, int startIndex, int length)
{
return string.Concat(
input[..startIndex],
input[startIndex..(startIndex + length)].ToLowerInvariant(),
input[(startIndex + length)..]);
}
}
Код: Выделить всё
| Method | Mean | Error | StdDev | Gen0 | Allocated |
|----------- |---------:|----------:|----------:|-------:|----------:|
| NoChange | 4.224 ns | 0.0521 ns | 0.0487 ns | - | - |
| WithChange | 9.460 ns | 0.1714 ns | 0.1603 ns | 0.0020 | 32 B |
Что это за магия?
Подробнее здесь: https://stackoverflow.com/questions/792 ... ate-memory