У меня есть следующий фрагмент кода, который добавит имя файла с текущей отметкой времени, и он работает нормально. Просто хочу убедиться, что это лучший способ добавления строк в С# 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];
Дайте определение «эффективный»?
@wohlstad в данном конкретном случае приведет к снижению производительности. stackoverflow.com/a/21131/9945524
@MarkCiliaVincenti смотрите мой первый комментарий о необходимости измерения, и я сомневаюсь, что в этом случае это действительно повысит производительность.
@wohlstad Я ответил тебе специально, потому что прочитал твой комментарий. Ранее вы писали, что «сомневаетесь, что это приведет к значительному улучшению», что очень похоже на то, что вы подразумеваете ожидание некоторого улучшения, просто незначительного. Это, конечно, не так. Без доступных бенчмарков правильным предположением было бы то, что это приведет к небольшому снижению производительности.
@MarkCiliaVincenti Я, безусловно, согласен с тем, что бенчмарк является обязательным, и, поскольку у меня нет конкретного опыта в конкатенации строк бенчмаркинга, я принимаю вашу интуицию насчет деградации.
Я добавил ориентир к своему ответу...
Строки неизменяемы. всякий раз, когда вы изменяете любую строку, она воссоздается в памяти. Следовательно, чтобы преодолеть это, вы всегда должны использовать 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 с умом, когда это имеет смысл (например, в циклах, которые добавляются к строке).
@MarkCiliaVincenti Он не спрашивал о преимуществах производительности. Он спросил, как добавить строку. Лучше внимательно читайте описание.
Так что, по-вашему, имелось в виду под «эффективным»?
Как насчет этого:
// 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, но я не уверен, что он более эффективен.
Никто не определил, что означает «эффективный» в данном контексте. @MarkCiliaVincenti
Вы правы, возможно, я ошибочно предположил, что это означает более быстрое и меньшее использование памяти.
Придется написать бенчмарк, чтобы доказать это. По крайней мере, это читабельно, но на самом деле есть ошибка с «.tar.gz», потому что методы Path будут учитывать только расширение «.gz» ... так что есть потенциал для улучшений.
Оригинал, однако, превращал бы «test.tar.gz» в «test_T135345667.gz»… так что в любом случае были бы некоторые разъяснения по необходимым требованиям.
Да, нам нужно написать тест, чтобы доказать это, но это хорошее обоснованное предположение, потому что, хотя исходный код разбивает строку один раз и использует эту операцию для получения обеих частей имени файла, этот код, который вы предоставили, разделит строку дважды.
Правильный. Теперь, если мы хотим выжать из этого самый последний кусочек, я бы предпочел переключиться на решение, основанное на Span<char>...
Странно, что в .NET нет ничего готового для анализа файлов/путей и предоставления всех частей, вычисляемых один раз.
Ну, строительные блоки есть. Не пропустил ничего подобного, тбх.
Я не уверен на 100%, но боюсь, что нет.
Вы можете посмотреть на StringBuilder: learn.microsoft.com/en-us/dotnet/api/… . Однако в этом простом случае я сомневаюсь, что это даст значительное улучшение. Как всегда в случае с производительностью важно измерять и сравнивать.