CA1835:在基于流的类中,首选 ReadAsync/WriteAsync 方法的基于内存的重载

属性
类型名称 PreferStreamAsyncMemoryOverloads
规则 ID CA1835
标题 在基于流的类中,首选 ReadAsync/WriteAsync 方法的基于内存的重载
类别 “性能”
修复是中断修复还是非中断修复 非中断
在 .NET 8 中默认启用 作为建议

原因

此规则查找 ReadAsyncWriteAsync 的基于字节数组的方法重载的等待调用,并建议改为使用基于内存的方法重载,因为它们的效率更高。

规则说明

基于内存的方法重载具有比基于字节数组的重载更有效的内存使用。

此规则适用于从 Stream 继承的任何类的 ReadAsyncWriteAsync 调用。

仅当方法前面带有 await 关键字时,此规则才有效。

检测到的方法 建议的方法
ReadAsync(Byte[], Int32, Int32, CancellationToken) ReadAsync(Memory<Byte>, CancellationToken)
ReadAsync(Byte[], Int32, Int32) CancellationToken 设置为 default(在 C# 中)或 Nothing(在 Visual Basic 中)的 ReadAsync(Memory<Byte>, CancellationToken)
WriteAsync(Byte[], Int32, Int32, CancellationToken) WriteAsync(ReadOnlyMemory<Byte>, CancellationToken)
WriteAsync(Byte[], Int32, Int32) CancellationToken 设置为 default(在 C# 中)或 Nothing(在 Visual Basic 中)的 WriteAsync(ReadOnlyMemory<Byte>, CancellationToken)

重要

确保将 offsetcount 整数参数传递到创建的 MemoryReadOnlyMemory 实例。

注意

规则 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

此规则可以检测 ReadAsyncWriteAsync 方法的多种冲突。 下面是此规则可检测到的情况示例:

示例 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

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

另请参阅