次の方法で共有


CA1844: "Stream" のサブクラス化時に、非同期メソッドのメモリベースのオーバーライドを提供する

プロパティ
ルール ID CA1844
Title 'Stream' をサブクラス化するときの非同期メソッドによるメモリ ベースのオーバーライドを指定する
[カテゴリ] パフォーマンス
修正が中断ありか中断なしか なし
.NET 10 で既定で有効 提案として

原因

Stream の派生型により、ReadAsync(Byte[], Int32, Int32, CancellationToken) はオーバーライドされますが、ReadAsync(Memory<Byte>, CancellationToken) はオーバーライドされません。 または、Stream の派生型により、WriteAsync(Byte[], Int32, Int32, CancellationToken) はオーバーライドされますが、WriteAsync(ReadOnlyMemory<Byte>, CancellationToken) はオーバーライドされません。

規則の説明

パフォーマンスを向上させるためにメモリベースの ReadAsync および WriteAsync メソッドが追加され、複数の方法でそれが実現されます。

  • それぞれ、ValueTaskValueTask<int> ではなく、TaskTask<int> を返します。
  • これにより、配列への余分なコピーを実行することなく、任意の種類のバッファーを渡すことができます。

これらのパフォーマンス上の利点を実現するには、Stream の派生型で独自のメモリベースの実装を提供する必要があります。 そうしないと、配列ベースの実装を呼び出すために、既定の実装が強制されて配列にメモリがコピーされる結果、パフォーマンスが低下します。 配列に基づいていない Memory<T> または ReadOnlyMemory<T> のインスタンスを呼び出し元が渡す場合は、パフォーマンスがさらに影響を受けます。

違反の修正方法

違反を修正する最も簡単な方法は、配列ベースの実装をメモリベースの実装として書き換えた後、メモリベースのメソッドの観点から配列ベースのメソッドを実装することです。

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

どのようなときに警告を抑制するか

次のいずれかの状況が当てはまる場合は、この規則による警告を抑制しても問題ありません。

  • パフォーマンスの低下が問題ではない。
  • Stream サブクラスで配列ベースのメソッドだけが使用されることがわかっている。
  • Stream サブクラスに、メモリベースのバッファーをサポートしない依存関係がある。

警告を抑制する

単一の違反を抑制するだけの場合は、ソース ファイルにプリプロセッサ ディレクティブを追加して無効にしてから、規則をもう一度有効にします。

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

ファイル、フォルダー、またはプロジェクトの規則を無効にするには、noneでその重要度を に設定します。

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

詳細については、「コード分析の警告を抑制する方法」を参照してください。

関連項目