Эффективный способ объединения строк с помощью С#

У меня есть следующий фрагмент кода, который добавит имя файла с текущей отметкой времени, и он работает нормально. Просто хочу убедиться, что это лучший способ добавления строк в С# 10, если нет, то как мы можем сделать приведенный ниже код более эффективным? пример: testfile.txt ->o/p testfile_timestamp.txt

 string[] strName = formFile.FileName.Split('.');
                string updatedFilename = strName[0] + "_"
                   + DateTime.Now.ToUniversalTime().ToString("THHmmssfff") + "." +
                   strName[strName.Length - 1];

Вы можете посмотреть на StringBuilder: learn.microsoft.com/en-us/dotnet/api/… . Однако в этом простом случае я сомневаюсь, что это даст значительное улучшение. Как всегда в случае с производительностью важно измерять и сравнивать.

wohlstad 10.01.2023 13:28
stackoverflow.com/questions/21078/…
Denis Schaf 10.01.2023 13:33

Дайте определение «эффективный»?

Fildor 10.01.2023 13:35

@wohlstad в данном конкретном случае приведет к снижению производительности. stackoverflow.com/a/21131/9945524

Mark Cilia Vincenti 10.01.2023 13:49

@MarkCiliaVincenti смотрите мой первый комментарий о необходимости измерения, и я сомневаюсь, что в этом случае это действительно повысит производительность.

wohlstad 10.01.2023 13:50

@wohlstad Я ответил тебе специально, потому что прочитал твой комментарий. Ранее вы писали, что «сомневаетесь, что это приведет к значительному улучшению», что очень похоже на то, что вы подразумеваете ожидание некоторого улучшения, просто незначительного. Это, конечно, не так. Без доступных бенчмарков правильным предположением было бы то, что это приведет к небольшому снижению производительности.

Mark Cilia Vincenti 10.01.2023 13:56

@MarkCiliaVincenti Я, безусловно, согласен с тем, что бенчмарк является обязательным, и, поскольку у меня нет конкретного опыта в конкатенации строк бенчмаркинга, я принимаю вашу интуицию насчет деградации.

wohlstad 10.01.2023 13:59

Я добавил ориентир к своему ответу...

Fildor 10.01.2023 14:21
Ускорьте разработку веб-приложений Laravel с помощью этих бесплатных стартовых наборов
Ускорьте разработку веб-приложений Laravel с помощью этих бесплатных стартовых наборов
Laravel - это мощный PHP-фреймворк, используемый для создания масштабируемых и надежных веб-приложений. Одним из преимуществ Laravel является его...
Что такое двойные вопросительные знаки (??) в JavaScript?
Что такое двойные вопросительные знаки (??) в JavaScript?
Как безопасно обрабатывать неопределенные и нулевые значения в коде с помощью Nullish Coalescing
Создание ресурсов API Laravel: Советы по производительности и масштабируемости
Создание ресурсов API Laravel: Советы по производительности и масштабируемости
Создание API-ресурса Laravel может быть непростой задачей. Она требует глубокого понимания возможностей Laravel и лучших практик, чтобы обеспечить...
Как сделать компонент справочного центра с помощью TailwindCSS
Как сделать компонент справочного центра с помощью TailwindCSS
Справочный центр - это веб-сайт, где клиенты могут найти ответы на свои вопросы и решения своих проблем. Созданный для решения многих распространенных...
Асинхронная передача данных с помощью sendBeacon в JavaScript
Асинхронная передача данных с помощью sendBeacon в JavaScript
В современных веб-приложениях отправка данных из JavaScript на стороне клиента на сервер является распространенной задачей. Одним из популярных...
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Отказ от ответственности: Эта статья предназначена только для демонстрации и не должна использоваться в качестве инвестиционного совета.
0
8
73
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Строки неизменяемы. всякий раз, когда вы изменяете любую строку, она воссоздается в памяти. Следовательно, чтобы преодолеть это, вы всегда должны использовать StringBuilder. Пример кода показан ниже:

string[] strName = formFile.FileName.Split('.');
StringBuilder updatedFilename  = new StringBuilder();
updatedFilename.Append(strName[0]);
updatedFilename.Append("_");
updatedFilename.Append(DateTime.Now.ToUniversalTime().ToString("THHmmssfff"));
updatedFilename.Append(".");
updatedFilename.Append(strName[strName.Length - 1]);

// You can get this using `ToString()` method
string filename = updatedFilename.ToString();

Я не уверен, что в данном конкретном случае StringBuilder даст преимущество в производительности. На самом деле, я подозреваю, что если бы вы сравнили его, вы бы увидели, что он работает хуже. Вы не должны «всегда использовать StringBuilder», иначе иногда вы будете добавлять ненужные накладные расходы. Вы должны использовать StringBuilder с умом, когда это имеет смысл (например, в циклах, которые добавляются к строке).

Mark Cilia Vincenti 10.01.2023 13:40

@MarkCiliaVincenti Он не спрашивал о преимуществах производительности. Он спросил, как добавить строку. Лучше внимательно читайте описание.

HarrY 10.01.2023 13:44

Так что, по-вашему, имелось в виду под «эффективным»?

Mark Cilia Vincenti 10.01.2023 13:46

Как насчет этого:

// Just a stupid method name for demo, you'll find a better one :)
public static string DatifyFileName(string fileName)
{
// Use well tested Framework method to get filename without extension
    var nameWithoutExtension = System.IO.Path.GetFileNameWithoutExtension(fileName);
// Use well tested Framework method to get extension
    var extension = System.IO.Path.GetExtension(fileName);
// interpolate to get the desired output.
    return $"{nameWithoutExtension}_{DateTime.Now.ToUniversalTime().ToString("THHmmssfff")}{extension}";
}

Или, если вы знакомы с Span<char>:

public static string DatifyFileName(ReadOnlySpan<char> fileName)
{
    var lastDotIndex = fileName.LastIndexOf('.');
    //Maybe : if ( lastDotIndex < 0 ) throw ArgumentException("no extension found");
    var nameWithoutExtension = fileName[..lastDotIndex];
    var extension = fileName[lastDotIndex..];
    return $"{nameWithoutExtension}_{DateTime.Now.ToUniversalTime().ToString("THHmmssfff")}{extension}";
}

Скрипка


И просто для того, чтобы разжечь дискуссию :D ...

BenchmarkDotNet=v0.13.3, OS=Windows 10 (10.0.19044.2364/21H2/November2021Update)
Intel Core i9-10885H CPU 2.40GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK=7.0.101
  [Host]     : .NET 7.0.1 (7.0.122.56804), X64 RyuJIT AVX2
  DefaultJob : .NET 7.0.1 (7.0.122.56804), X64 RyuJIT AVX2


|           Method |       Mean |    Error |   StdDev | Ratio | RatioSD |   Gen0 | Allocated | Alloc Ratio |
|----------------- |-----------:|---------:|---------:|------:|--------:|-------:|----------:|------------:|
|     Interpolated |   906.7 ns | 16.92 ns | 16.61 ns |  1.08 |    0.02 | 0.0458 |     384 B |        1.66 |
| InterpolatedSpan |   842.0 ns | 13.06 ns | 12.22 ns |  1.00 |    0.00 | 0.0277 |     232 B |        1.00 |
|    StringBuilder | 1,010.8 ns |  6.70 ns |  5.94 ns |  1.20 |    0.02 | 0.1068 |     904 B |        3.90 |
|         Original |   960.0 ns | 18.68 ns | 19.19 ns |  1.14 |    0.03 | 0.0734 |     616 B |        2.66 |

// * Hints *
Outliers
  Benchmark.StringBuilder: Default -> 1 outlier  was  removed (1.03 us)
  Benchmark.Original: Default      -> 2 outliers were removed (1.03 us, 1.06 us)

// * Legends *
  Mean        : Arithmetic mean of all measurements
  Error       : Half of 99.9% confidence interval
  StdDev      : Standard deviation of all measurements
  Ratio       : Mean of the ratio distribution ([Current]/[Baseline])
  RatioSD     : Standard deviation of the ratio distribution ([Current]/[Baseline])
  Gen0        : GC Generation 0 collects per 1000 operations
  Allocated   : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
  Alloc Ratio : Allocated memory ratio distribution ([Current]/[Baseline])
  1 ns        : 1 Nanosecond (0.000000001 sec)

Это более аккуратный и надежный код, и он не захлебнется, например, файлами .tar.gz, но я не уверен, что он более эффективен.

Mark Cilia Vincenti 10.01.2023 13:42

Никто не определил, что означает «эффективный» в данном контексте. @MarkCiliaVincenti

Fildor 10.01.2023 13:44

Вы правы, возможно, я ошибочно предположил, что это означает более быстрое и меньшее использование памяти.

Mark Cilia Vincenti 10.01.2023 13:45

Придется написать бенчмарк, чтобы доказать это. По крайней мере, это читабельно, но на самом деле есть ошибка с «.tar.gz», потому что методы Path будут учитывать только расширение «.gz» ... так что есть потенциал для улучшений.

Fildor 10.01.2023 13:49

Оригинал, однако, превращал бы «test.tar.gz» в «test_T135345667.gz»… так что в любом случае были бы некоторые разъяснения по необходимым требованиям.

Fildor 10.01.2023 13:53

Да, нам нужно написать тест, чтобы доказать это, но это хорошее обоснованное предположение, потому что, хотя исходный код разбивает строку один раз и использует эту операцию для получения обеих частей имени файла, этот код, который вы предоставили, разделит строку дважды.

Mark Cilia Vincenti 10.01.2023 13:53

Правильный. Теперь, если мы хотим выжать из этого самый последний кусочек, я бы предпочел переключиться на решение, основанное на Span<char>...

Fildor 10.01.2023 13:54

Странно, что в .NET нет ничего готового для анализа файлов/путей и предоставления всех частей, вычисляемых один раз.

Mark Cilia Vincenti 10.01.2023 14:01

Ну, строительные блоки есть. Не пропустил ничего подобного, тбх.

Fildor 10.01.2023 14:05

Я не уверен на 100%, но боюсь, что нет.

Fildor 10.01.2023 14:10

Другие вопросы по теме