Partilhar via


CA1844: Fornecer substituições baseadas em memória de métodos assíncronos ao subclassificar 'Stream'

Propriedade valor
ID da regra CA1844
Título Fornecer substituições baseadas em memória de métodos assíncronos ao subclassificar 'Stream'
Categoria Desempenho
A correção causa interrupção ou não Ininterrupto
Habilitado por padrão no .NET 10 Como sugestão
Línguas aplicáveis C# e Visual Basic

Motivo

Um tipo derivado de Stream substitui ReadAsync(Byte[], Int32, Int32, CancellationToken) mas não substitui ReadAsync(Memory<Byte>, CancellationToken). Ou, um tipo derivado de Stream substitui WriteAsync(Byte[], Int32, Int32, CancellationToken), mas não substitui WriteAsync(ReadOnlyMemory<Byte>, CancellationToken).

Descrição da regra

Os métodos ReadAsync e WriteAsync baseados em memória foram adicionados para melhorar o desempenho, o que eles conseguem de várias formas.

  • Eles retornam ValueTask e ValueTask<int> em vez de Task e Task<int>, respectivamente.
  • Eles permitem que qualquer tipo de buffer seja passado sem precisar fazer uma cópia adicional para uma matriz.

Para obter esses benefícios de desempenho, os tipos que derivam de Stream devem fornecer sua própria implementação baseada em memória. Caso contrário, a implementação padrão será forçada a copiar a memória em uma matriz para chamar a implementação baseada em matriz, resultando em desempenho reduzido. Quando o chamador passa numa instância Memory<T> ou ReadOnlyMemory<T> que não é baseada em uma matriz, o desempenho é ainda mais afetado.

Como corrigir violações

A maneira mais fácil de corrigir violações é reescrever sua implementação baseada em matriz como uma implementação baseada em memória e, em seguida, implementar os métodos baseados em matriz em termos de métodos baseados em memória.

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

Quando suprimir avisos

É seguro suprimir um aviso desta regra se alguma das seguintes situações se aplicar:

  • O impacto no desempenho não é uma preocupação.
  • Você sabe que sua Stream subclasse usará apenas os métodos baseados em matriz.
  • Sua Stream subclasse tem dependências que não suportam buffers baseados em memória.

Suprimir um aviso

Se você quiser apenas suprimir uma única violação, adicione diretivas de pré-processador ao seu arquivo de origem para desativar e, em seguida, reativar a regra.

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

Para desabilitar a regra de um arquivo, pasta ou projeto, defina sua gravidade como none no arquivo de configuração.

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

Para obter mais informações, consulte Como suprimir avisos de análise de código.

Consulte também