Udostępnij za pomocą


CA1844: Podaj zastąpienia oparte na pamięci metod asynchronicznych podczas podklasowania "Stream"

Właściwości Wartość
Identyfikator reguły CA1844
Tytuł Udostępnianie przesłonięć opartych na pamięci metod asynchronicznych podczas podklasowania "Stream"
Kategoria Wydajność
Poprawka powodująca niezgodność lub niezgodność Niezgodność
Domyślnie włączone na platformie .NET 10 Jako sugestia

Przyczyna

Typ pochodzący z Stream przesłonięć ReadAsync(Byte[], Int32, Int32, CancellationToken) , ale nie zastępuje ReadAsync(Memory<Byte>, CancellationToken)elementu . Lub typ pochodzący z Stream przesłonięć WriteAsync(Byte[], Int32, Int32, CancellationToken) , ale nie zastępuje WriteAsync(ReadOnlyMemory<Byte>, CancellationToken).

Opis reguły

Metody i oparte na ReadAsyncWriteAsync pamięci zostały dodane w celu zwiększenia wydajności, co można osiągnąć na wiele sposobów:

  • Zwracają ValueTask odpowiednio wartości i ValueTask<int> zamiast Task i Task<int>.
  • Umożliwiają one przekazywanie dowolnego typu buforu bez konieczności wykonywania dodatkowej kopii do tablicy.

Aby zrealizować te korzyści z wydajności, typy pochodzące z Stream programu muszą zapewnić własną implementację opartą na pamięci. W przeciwnym razie domyślna implementacja zostanie zmuszona do skopiowania pamięci do tablicy w celu wywołania implementacji opartej na tablicy, co spowoduje zmniejszenie wydajności. Gdy obiekt wywołujący przechodzi w wystąpieniu Memory<T> lub ReadOnlyMemory<T> , które nie jest obsługiwane przez tablicę, wydajność będzie miała większy wpływ.

Jak naprawić naruszenia

Najprostszym sposobem naprawienia naruszeń jest przepisanie implementacji opartej na tablicy jako implementacji opartej na pamięci, a następnie zaimplementowanie metod opartych na tablicy pod względem metod opartych na pamięci.

Example

// This class violates the rule.
public class BadStream : Stream
{
    private readonly Stream _innerStream;

    public BadStream(Stream innerStream)
    {
        _innerStream = innerStream;
    }

    public override bool CanRead => _innerStream.CanRead;
    public override bool CanSeek => _innerStream.CanSeek;
    public override bool CanWrite => _innerStream.CanWrite;
    public override long Length => _innerStream.Length;
    public override long Position { get => _innerStream.Position; set => _innerStream.Position = value; }

    public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
        // ...
        return await _innerStream.ReadAsync(buffer, offset, count, cancellationToken);
    }

    public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
        // ...
        await _innerStream.WriteAsync(buffer, offset, count, cancellationToken);
    }

    // Other required overrides
    public override void Flush() => _innerStream.Flush();
    public override int Read(byte[] buffer, int offset, int count) => _innerStream.Read(buffer, offset, count);
    public override long Seek(long offset, SeekOrigin origin) => _innerStream.Seek(offset, origin);
    public override void SetLength(long value) => _innerStream.SetLength(value);
    public override void Write(byte[] buffer, int offset, int count) => _innerStream.Write(buffer, offset, count);
}

// This class satisfies the rule.
public class GoodStream : Stream
{
    private readonly Stream _innerStream;

    public GoodStream(Stream innerStream)
    {
        _innerStream = innerStream;
    }

    public override bool CanRead => _innerStream.CanRead;
    public override bool CanSeek => _innerStream.CanSeek;
    public override bool CanWrite => _innerStream.CanWrite;
    public override long Length => _innerStream.Length;
    public override long Position { get => _innerStream.Position; set => _innerStream.Position = value; }

    public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
    {
        // ...
        return await _innerStream.ReadAsync(buffer, cancellationToken);
    }

    public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
    {
        // ...
        await _innerStream.WriteAsync(buffer, cancellationToken);
    }

    public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
        return await this.ReadAsync(buffer.AsMemory(offset, count), cancellationToken);
    }

    public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
        await this.WriteAsync(buffer.AsMemory(offset, count), cancellationToken);
    }

    // Other required overrides
    public override void Flush() => _innerStream.Flush();
    public override int Read(byte[] buffer, int offset, int count) => _innerStream.Read(buffer, offset, count);
    public override long Seek(long offset, SeekOrigin origin) => _innerStream.Seek(offset, origin);
    public override void SetLength(long value) => _innerStream.SetLength(value);
    public override void Write(byte[] buffer, int offset, int count) => _innerStream.Write(buffer, offset, count);
}

Kiedy pomijać ostrzeżenia

Można bezpiecznie pominąć ostrzeżenie z tej reguły, jeśli ma zastosowanie którakolwiek z następujących sytuacji:

  • Osiągnięcie wydajności nie jest problemem.
  • Wiesz, że podklasa Stream będzie używać tylko metod opartych na tablicy.
  • Podklasa Stream ma zależności, które nie obsługują opartych na pamięci.

Pomijanie ostrzeżenia

Jeśli chcesz po prostu pominąć pojedyncze naruszenie, dodaj dyrektywy preprocesora do pliku źródłowego, aby wyłączyć, a następnie ponownie włączyć regułę.

#pragma warning disable CA1844
// The code that's violating the rule is on this line.
#pragma warning restore CA1844

Aby wyłączyć regułę dla pliku, folderu lub projektu, ustaw jego ważność na none w pliku konfiguracji.

[*.{cs,vb}]
dotnet_diagnostic.CA1844.severity = none

Aby uzyskać więcej informacji, zobacz Jak pominąć ostrzeżenia dotyczące analizy kodu.

Zobacz też