Поделиться через


CA1835: предпочитать перегрузки методов ReadAsync/WriteAsync, основанные на памяти, в классах, основанных на потоках

Свойство Значение
Имя типа PreferStreamAsyncMemoryOverloads
Идентификатор правила CA1835
Заголовок Предпочитайте перегрузки на основе памяти методов ReadAsync/WriteAsync в классах на основе потоков
Категория Производительность
Исправление является критическим или не критическим Не критическое
Включен по умолчанию в .NET 8 Как предложение

Причина

Это правило находит ожидающие вызовы перегрузок методов на основе массивов байтов для ReadAsync и WriteAsync и предлагает использовать перегрузки методов на основе памяти, так как они более эффективны.

Описание правила

Перегрузки методов на основе памяти используют память более эффективно по сравнению с перегрузками методов на основе массивов байтов.

Правило работает с вызовами ReadAsync и WriteAsync любого класса, наследуемого от Stream.

Правило работает только в том случае, если перед методом стоит ключевое слово await.

Обнаруженный метод Предлагаемый метод
ReadAsync(Byte[], Int32, Int32, CancellationToken) ReadAsync(Memory<Byte>, CancellationToken)
ReadAsync(Byte[], Int32, Int32) ReadAsync(Memory<Byte>, CancellationToken) с параметром CancellationToken, имеющим значение default в C# или Nothing в Visual Basic.
WriteAsync(Byte[], Int32, Int32, CancellationToken) WriteAsync(ReadOnlyMemory<Byte>, CancellationToken)
WriteAsync(Byte[], Int32, Int32) WriteAsync(ReadOnlyMemory<Byte>, CancellationToken) с параметром CancellationToken, имеющим значение default в C# или Nothing в Visual Basic.

Важно!

Не забудьте передать целочисленные аргументы offset и count в созданные экземпляры Memory или ReadOnlyMemory.

Примечание.

Правило CA1835 доступно во всех версиях .NET, в которых имеются перегрузки на основе памяти:

  • .NET Standard 2.1 и более поздние версии.
  • .NET Core 2.1 и более поздние версии.

Устранение нарушений

Вы можете устранить их вручную или позволить Visual Studio сделать это автоматически, наведя указатель мыши на лампочку рядом с вызовом метода и выбрав предлагаемое изменение. Пример:

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

Правило может обнаруживать различные нарушения для методов ReadAsync и WriteAsync. Ниже приведены примеры случаев, которые может обнаружить это правило:

Пример 1

Вызовы ReadAsync с аргументом 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);
        }
    }
}

Исправление:

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

Пример 2

Вызовы WriteAsync с аргументом 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);
        }
    }
}

Исправление:

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

Пример 3

Вызовы с 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);
        }
    }
}

Исправление:

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

Случаи, не являющиеся нарушениями

Ниже приведены некоторые примеры вызовов, в которых правило не будет использовано.

Возвращаемое значение не ожидается, а сохраняется в переменной Task:

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

Возвращаемое значение не ожидается, а возвращается методом переноса:

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

Возвращаемое значение используется для вызова ContinueWith, который является ожидаемым методом:

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 => { /* ... */ });
        }
    }
}

Когда лучше отключить предупреждения

Нарушение этого правила можно подавить, если вы не беспокоитесь о повышении производительности при чтении или записи буферов в классах, основанных на потоках.

Отключение предупреждений

Если вы просто хотите отключить одно нарушение, добавьте директивы препроцессора в исходный файл, чтобы отключить и повторно включить правило.

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

Чтобы отключить правило для файла, папки или проекта, задайте его серьезность none в файле конфигурации.

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

Дополнительные сведения см. в разделе Практическое руководство. Скрытие предупреждений анализа кода.

См. также