Teilen über


CA1844: Arbeitsspeicherbasierte asynchrone Methoden beim Erstellen von Unterklassen von „Stream“ außer Kraft setzen

Eigenschaft Wert
Regel-ID CA1844
Titel Arbeitsspeicherbasierte asynchrone Methoden beim Erstellen von Unterklassen von „Stream“ außer Kraft setzen
Kategorie Leistung
Fix führt oder führt nicht zur Unterbrechung Nicht unterbrechend
Standardmäßig in .NET 10 aktiviert Als Vorschlag

Ursache

Ein von Stream abgeleiteter Typ überschreibt ReadAsync(Byte[], Int32, Int32, CancellationToken), aber nicht ReadAsync(Memory<Byte>, CancellationToken). Oder ein von Stream abgeleiteter Typ überschreibt WriteAsync(Byte[], Int32, Int32, CancellationToken), aber nicht WriteAsync(ReadOnlyMemory<Byte>, CancellationToken).

Regelbeschreibung

Die speicherbasierten Methoden ReadAsync und WriteAsync wurden hinzugefügt, um die Leistung zu verbessern. Dies wird von ihnen auf verschiedene Weise erreicht:

  • Sie geben ValueTask bzw. ValueTask<int> anstelle von Task bzw. Task<int> zurück.
  • Sie ermöglichen das Übergeben beliebiger Puffertypen, ohne dass eine zusätzliche Kopie in ein Array erfolgen muss.

Um diese Leistungsvorteile zu nutzen, müssen von Stream abgeleitete Typen eine eigene speicherbasierte Implementierung bereitstellen. Andernfalls wird die Standardimplementierung gezwungen, den Arbeitsspeicher in ein Array zu kopieren, um die arraybasierte Implementierung aufzurufen, was zu einer verringerten Leistung führt. Wenn der Aufrufer eine Memory<T>- oder ReadOnlyMemory<T>-Instanz übergibt, die nicht von einem Array unterstützt wird, wird die Leistung stärker beeinträchtigt.

Behandeln von Verstößen

Die einfachste Möglichkeit zum Beheben von Verstößen besteht darin, die arraybasierte Implementierung in eine speicherbasierte Implementierung umzuschreiben und die arraybasierten Methoden anschließend in Bezug auf die speicherbasierten Methoden zu implementieren.

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);
}

Wann sollten Warnungen unterdrückt werden?

In den folgenden Situationen ist es sicher, eine Warnung aus dieser Regel zu unterdrücken:

  • Leistungseinbußen sind kein Problem.
  • Sie wissen, dass Ihre Stream-Unterklasse grundsätzlich nur die arraybasierten Methoden verwendet.
  • Ihre Stream-Unterklasse verfügt über Abhängigkeiten, die keine speicherbasierten Puffer unterstützen.

Unterdrücken einer Warnung

Um nur eine einzelne Verletzung zu unterdrücken, fügen Sie der Quelldatei Präprozessoranweisungen hinzu, um die Regel zu deaktivieren und dann wieder zu aktivieren.

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

Um die Regel für eine Datei, einen Ordner oder ein Projekt zu deaktivieren, legen Sie den Schweregrad in der none auf fest.

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

Weitere Informationen finden Sie unter Vorgehensweise: Unterdrücken von Codeanalyse-Warnungen.

Siehe auch