Результаты полного code review библиотеки Ramstack.FileSystem. Документ служит планом работ по исправлению найденных проблем.
Файл: src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs, строки 336–337
Серьёзность: Средняя
Метод WriteAllLinesAsync принимает CancellationToken, но не проверяет отмену внутри цикла foreach. Это несовместимо с ReadAllLinesAsync (строка 147), где cancellationToken.ThrowIfCancellationRequested() вызывается корректно.
Сейчас:
foreach (var line in contents)
await writer.WriteLineAsync(line).ConfigureAwait(false);Исправление:
foreach (var line in contents)
{
cancellationToken.ThrowIfCancellationRequested();
await writer.WriteLineAsync(line).ConfigureAwait(false);
}Файл: src/Ramstack.FileSystem.Abstractions/Utilities/EnumerableExtensions.cs, строка 51
Серьёзность: Средняя
MoveNextAsync при отмене возвращает false вместо выброса OperationCanceledException. Это маскирует отмену под нормальное завершение перечисления.
Сейчас:
var result = !cancellationToken.IsCancellationRequested && enumerator.MoveNext();
return new ValueTask<bool>(result);Исправление:
cancellationToken.ThrowIfCancellationRequested();
return new ValueTask<bool>(enumerator.MoveNext());Файл: src/Ramstack.FileSystem.Composite/CompositeFileSystem.cs, строки 36–37
Серьёзность: Средняя
Конструктор, принимающий IEnumerable<IVirtualFileSystem>, не проверяет аргумент на null, в отличие от params-перегрузки (строка 28).
Сейчас:
public CompositeFileSystem(IEnumerable<IVirtualFileSystem> fileSystems) =>
InternalFileSystems = fileSystems.ToArray();Исправление:
public CompositeFileSystem(IEnumerable<IVirtualFileSystem> fileSystems)
{
ArgumentNullException.ThrowIfNull(fileSystems);
InternalFileSystems = fileSystems.ToArray();
}Файл: src/Ramstack.FileSystem.Amazon/S3UploadStream.cs, строки 153–160
Серьёзность: Низкая (для Stream — no-op, но нарушение паттерна)
Метод Dispose(bool) делегирует работу DisposeAsync(), но не вызывает base.Dispose(disposing).
Исправление:
protected override void Dispose(bool disposing)
{
if (disposing)
{
using var scope = NullSynchronizationContext.CreateScope();
DisposeAsync().AsTask().Wait();
}
base.Dispose(disposing);
}Файл: src/Ramstack.FileSystem.Abstractions/VirtualFileSystemExtensions.cs, строка 192
Сейчас: "Asynchronously writes the specified string to the specified the file."
Исправление: "Asynchronously writes the specified string to the specified file."
Файл: src/Ramstack.FileSystem.Physical/PhysicalFileSystem.cs, строка 24
Сейчас: "The physical path of root the directory."
Исправление: "The physical path of the root directory."
Основной вариант (подавляющее большинство): "An optional cancellation token to cancel the operation."
Нестандартные варианты, требующие унификации:
| Файл | Строка | Текущий текст |
|---|---|---|
VirtualFileSystemExtensions.cs |
27 | "The optional cancellation token used for canceling the operation." |
VirtualFileSystemExtensions.cs |
40 | "The optional cancellation token used for canceling the operation." |
VirtualFileSystemExtensions.cs |
276 | "A cancellation token to cancel the operation." |
VirtualFileSystemExtensions.cs |
302 | "An optional cancellation token to cancel the operation. Defaults to <see cref="CancellationToken.None"/>." |
VirtualFileSystemExtensions.cs |
316 | "A token to cancel the operation. Defaults to <see cref="CancellationToken.None"/>." |
VirtualFileSystemExtensions.cs |
335 | "A cancellation token to cancel the operation." |
VirtualFileExtensions.cs |
384 | "An optional cancellation token to cancel the operation. Defaults to <see cref="CancellationToken.None"/>." |
VirtualNode.cs |
65 | "A cancellation token to cancel the operation." |
VirtualNode.cs |
120 | "A cancellation token to cancel the operation." |
GcsFile.cs |
145 | "A cancellation token to cancel the operation." |
AzureFile.cs |
120 | "A cancellation token to cancel the operation." |
S3File.cs |
137 | "A cancellation token to cancel the operation." |
S3UploadStream.cs |
218 | "A cancellation token to cancel the operation." |
S3UploadStream.cs |
272 | "A cancellation token to cancel the operation." |
AmazonS3FileSystem.cs |
111 | "A cancellation token to cancel the operation." |
AmazonS3FileSystem.cs |
122 | "A cancellation token to cancel the operation." |
Рекомендация: Унифицировать все до "An optional cancellation token to cancel the operation." — без суффикса "Defaults to...".
Три разных варианта:
| Текст | Файлы и строки |
|---|---|
"The character encoding to use." |
VirtualFileExtensions.cs:29, VirtualFileSystemExtensions.cs:39 |
"The encoding applied to the contents." |
VirtualFileExtensions.cs:69,133, VirtualFileSystemExtensions.cs:115,142 |
"The encoding to apply to the string." |
VirtualFileExtensions.cs:272,297,326, VirtualFileSystemExtensions.cs:183,210,237 |
Рекомендация: Унифицировать до "The encoding to use." или разграничить: для чтения — "The encoding applied to the contents.", для записи — "The encoding to apply to the string.".
Файл: src/Ramstack.FileSystem.Abstractions/VirtualFile.cs, строки 188–190
В VirtualFile.WriteCoreAsync remarks используют "should":
<item><description>If the file does not exist, it should be created.</description></item>
<item><description>...the existing file should be overwritten.</description></item>
<item><description>...an exception should be thrown.</description></item>В VirtualFileSystemExtensions.cs (строки 322–324) аналогичный remarks использует "will":
<item><description>If the file does not exist, it will be created.</description></item>Рекомендация: Для абстрактного метода (WriteCoreAsync) "should" может быть допустимо (предписание реализации), но для единообразия лучше использовать "must" или "will".
Файл: src/Ramstack.FileSystem.Abstractions/VirtualFileSystemExtensions.cs, строки 29, 42
Используется "A task representing the asynchronous operation and returns..." — это не стандартный формат и не упоминает ValueTask/Task через <see cref="..."/>.
Остальные методы используют:
/// A <see cref="ValueTask"/> representing the asynchronous operation.Рекомендация: Унифицировать до:
/// A <see cref="Task{TResult}"/> representing the asynchronous operation.
/// The task result contains a <see cref="StreamReader"/> that reads from the text file.Разные формулировки для одного и того же:
| Файл | Текст |
|---|---|
CompositeFile.cs:21 |
"The <see cref="VirtualFile"/> to wrap." |
GlobbingFile.cs:21 |
"The <see cref="VirtualFile"/> instance to wrap." |
ReadonlyFile.cs:18 |
"The <see cref="VirtualFile"/> instance to wrap." |
PrefixedFile.cs:20 |
"The underlying <see cref="VirtualFile"/> that this instance wraps." |
SubFile.cs:19 |
"The underlying <see cref="VirtualFile"/> instance to wrap." |
Рекомендация: Унифицировать, например, до "The <see cref="VirtualFile"/> instance to wrap.".
Два варианта:
| Текст | Строки |
|---|---|
"The file system to use." |
Большинство методов |
"The <see cref="IVirtualFileSystem"/> instance." |
Строки 299, 312 (CopyFileAsync) |
Рекомендация: Унифицировать до "The file system to use.".
- Исправить опечатку "the specified the file" (
VirtualFileSystemExtensions.cs:192) - Исправить опечатку "root the directory" (
PhysicalFileSystem.cs:24) - Добавить
cancellationToken.ThrowIfCancellationRequested()вWriteAllLinesAsync(VirtualFileExtensions.cs:336) - Исправить
AsyncEnumeratorAdapter.MoveNextAsync— бросатьOperationCanceledExceptionвместо возвратаfalse(EnumerableExtensions.cs:51) - Добавить
ArgumentNullException.ThrowIfNullв конструкторCompositeFileSystem(CompositeFileSystem.cs:36)
- Унифицировать описания
cancellationToken(16 мест — см. раздел 3.1) - Унифицировать описания
encoding(12 мест — см. раздел 3.2) - Исправить "should" → "will"/"must" в
VirtualFile.cs:188-190 - Унифицировать
<returns>дляOpenTextAsync(VirtualFileSystemExtensions.cs:29,42) - Унифицировать описания параметра
fileв обёрточных классах (5 мест — см. раздел 3.5) - Унифицировать описания параметра
fs(VirtualFileSystemExtensions.cs:299,312)
- Добавить
base.Dispose(disposing)вS3UploadStream.Dispose(S3UploadStream.cs:153)