CA1835: Dar preferência às sobrecargas baseadas em memória dos métodos ReadAsync/WriteAsync em classes baseadas em fluxo

Property Valor
Nome do tipo PreferStreamAsyncMemoryOverloads
ID da regra CA1835
Título Dar preferência às sobrecargas baseadas em memória dos métodos ReadAsync/WriteAsync em classes baseadas em fluxo
Categoria Desempenho
Correção interruptiva ou sem interrupção Sem interrupção
Habilitado por padrão no .NET 8 Como sugestão

Causa

Essa regra localiza as invocações aguardadas das sobrecargas de método baseadas em matriz de bytes para ReadAsync e WriteAsync e sugere o uso das sobrecargas de método baseadas em memória, pois elas são mais eficientes.

Descrição da regra

As sobrecargas de método baseadas em memória têm um uso de memória mais eficiente do que as baseadas em matriz de bytes.

A regra funciona nas invocações de ReadAsync e WriteAsync de qualquer classe que herde de Stream.

Ela só funciona quando o método é precedido pela palavra-chave await.

Método detectado Método sugerido
ReadAsync(Byte[], Int32, Int32, CancellationToken) ReadAsync(Memory<Byte>, CancellationToken)
ReadAsync(Byte[], Int32, Int32) ReadAsync(Memory<Byte>, CancellationToken) com CancellationToken definido como default em C# ou Nothing em Visual Basic.
WriteAsync(Byte[], Int32, Int32, CancellationToken) WriteAsync(ReadOnlyMemory<Byte>, CancellationToken)
WriteAsync(Byte[], Int32, Int32) WriteAsync(ReadOnlyMemory<Byte>, CancellationToken) com CancellationToken definido como default em C# ou Nothing em Visual Basic.

Importante

Transmita os argumentos de inteiros offset e count para as instâncias criadas de Memory e ReadOnlyMemory.

Observação

A regra CA1835 está disponível em todas as versões do .NET em que as sobrecargas baseadas em memória estão disponíveis:

  • .NET Standard 2.1 e superior.
  • .NET Core 2.1 e superior.

Como corrigir violações

Você pode corrigi-las manualmente ou optar por permitir que o Visual Studio faça isso por você, posicionando o mouse sobre a lâmpada que aparece ao lado da invocação do método e selecionando a alteração sugerida. Exemplo:

Code fix for CA1835 - Prefer the memory-based overloads of ReadAsync/WriteAsync methods in stream-based classes

A regra pode detectar uma variedade de violações para os métodos ReadAsync e WriteAsync. Estes são exemplos dos casos que a regra pode detectar:

Exemplo 1

Invocações de ReadAsync, sem e com um argumento CancellationToken:

using System;
using System.IO;
using System.Threading;

class MyClass
{
    public async void MyMethod(CancellationToken ct)
    {
        using (FileStream s = new FileStream("path.txt", FileMode.Create))
        {
            byte[] buffer = new byte[s.Length];
            await s.ReadAsync(buffer, 0, buffer.Length);
            await s.ReadAsync(buffer, 0, buffer.Length, ct);
        }
    }
}

Correção:

using System;
using System.IO;
using System.Threading;

class MyClass
{
    public async void MyMethod(CancellationToken ct)
    {
        using (FileStream s = new FileStream("path.txt", FileMode.Create))
        {
            byte[] buffer = new byte[s.Length];
            await s.ReadAsync(buffer.AsMemory(0, buffer.Length));
            await s.ReadAsync(buffer.AsMemory(0, buffer.Length), ct);
        }
    }
}

Exemplo 2

Invocações de WriteAsync, sem e com um argumento CancellationToken:

using System;
using System.IO;
using System.Threading;

class MyClass
{
    public async void MyMethod(CancellationToken ct)
    {
        using (FileStream s = File.Open("path.txt", FileMode.Open))
        {
            byte[] buffer = { 0xBA, 0x5E, 0xBA, 0x11, 0xF0, 0x07, 0xBA, 0x11 };
            await s.WriteAsync(buffer, 0, buffer.Length);
            await s.WriteAsync(buffer, 0, buffer.Length, ct);
        }
    }
}

Correção:

using System;
using System.IO;
using System.Threading;

class MyClass
{
    public async void MyMethod()
    {
        using (FileStream s = File.Open("path.txt", FileMode.Open))
        {
            byte[] buffer = { 0xBA, 0x5E, 0xBA, 0x11, 0xF0, 0x07, 0xBA, 0x11 };
            await s.WriteAsync(buffer.AsMemory(0, buffer.Length));
            await s.WriteAsync(buffer.AsMemory(0, buffer.Length), ct);
        }
    }
}

Exemplo 3

Invocações com ConfigureAwait:

using System;
using System.IO;
using System.Threading;

class MyClass
{
    public async void MyMethod()
    {
        using (FileStream s = File.Open("path.txt", FileMode.Open))
        {
            byte[] buffer1 = { 0xBA, 0x5E, 0xBA, 0x11, 0xF0, 0x07, 0xBA, 0x11 };
            await s.WriteAsync(buffer1, 0, buffer1.Length).ConfigureAwait(false);

            byte[] buffer2 = new byte[s.Length];
            await s.ReadAsync(buffer2, 0, buffer2.Length).ConfigureAwait(true);
        }
    }
}

Correção:

using System;
using System.IO;
using System.Threading;

class MyClass
{
    public async void MyMethod()
    {
        using (FileStream s = File.Open("path.txt", FileMode.Open))
        {
            byte[] buffer1 = { 0xBA, 0x5E, 0xBA, 0x11, 0xF0, 0x07, 0xBA, 0x11 };
            await s.WriteAsync(buffer1.AsMemory(0, buffer1.Length)).ConfigureAwait(false);

            byte[] buffer2 = new byte[s.Length];
            await s.ReadAsync(buffer2.AsMemory(0, buffer.Length)).ConfigureAwait(true);
        }
    }
}

Não violações

Veja a seguir alguns exemplos de invocações em que a regra não será disparada.

O valor retornado é salvo em uma variável Task em vez de ser aguardado:

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

class MyClass
{
    public void MyMethod()
    {
        byte[] buffer = { 0xBA, 0x5E, 0xBA, 0x11, 0xF0, 0x07, 0xBA, 0x11 };
        using (FileStream s = new FileStream("path.txt", FileMode.Create))
        {
            Task t = s.WriteAsync(buffer, 0, buffer.Length);
        }
    }
}

O valor retornado é retornado pelo método de encapsulamento em vez de ser aguardado:

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

class MyClass
{
    public Task MyMethod(FileStream s, byte[] buffer)
    {
        return s.WriteAsync(buffer, 0, buffer.Length);
    }
}

O valor retornado é usado para chamar ContinueWith, que é o método que está sendo aguardado:

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

class MyClass
{
    public void MyMethod()
    {
        byte[] buffer = { 0xBA, 0x5E, 0xBA, 0x11, 0xF0, 0x07, 0xBA, 0x11 };
        using (FileStream s = new FileStream("path.txt", FileMode.Create))
        {
            await s.WriteAsync(buffer, 0, buffer.Length).ContinueWith(c => { /* ... */ });
        }
    }
}

Quando suprimir avisos

É seguro suprimir uma violação dessa regra se você não está preocupado em aprimorar o desempenho ao ler ou gravar buffers em classes baseadas em fluxo.

Suprimir um aviso

Para suprimir apenas uma violação, adicione diretivas de pré-processador ao arquivo de origem a fim de desabilitar e, em seguida, reabilitar a regra.

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

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

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

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

Confira também