CA1844:对“流”进行子分类时,提供异步方法的基于内存的重写

属性
规则 ID CA1844
标题 在对“Stream”进行子类化时提供异步方法的基于内存的重写
类别 “性能”
修复是中断修复还是非中断修复 非中断
在 .NET 10 中默认启用 作为建议

原因

派生自 Stream 的类型会重写 ReadAsync(Byte[], Int32, Int32, CancellationToken),但不会重写 ReadAsync(Memory<Byte>, CancellationToken)。 或,派生自 Stream 的类型会重写 WriteAsync(Byte[], Int32, Int32, CancellationToken),但不会重写 WriteAsync(ReadOnlyMemory<Byte>, CancellationToken)

规则说明

添加了基于内存的 ReadAsyncWriteAsync 方法来提高性能,这些方法的实现方式有多种:

  • 它们分别返回 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

有关详细信息,请参阅如何禁止显示代码分析警告

另请参阅