何时使用ReadOnlySpan<T>替代显式/重载类型?
文本转十六进制字符串的API设计:Span vs 显式重载?
我们需要实现纯文本字符串到十六进制字符串的转换,比如把"Hello, World!"转为"48656c6c6f2c20576f726c6421",这个过程分为两步:
- 将纯文本字符串转换为字节数组
- 将字节数组转换为十六进制字符串
基础实现代码如下:
string plainText = "Hello, World!"; byte[] bytes = Encoding.Default.GetBytes(plainText); string hexadecimalString = Convert.ToHexString(bytes);
.NET Core 2.1之前的重载实现
在.NET Core 2.1之前,封装这个功能最灵活的方式是为string和char[]提供重载方法,示例代码如下:
public static string ToHexString(string value, Encoding? encoding = null) => ToHexString(value.ToCharArray(), encoding); public static string ToHexString(char[] value, Encoding? encoding = null) => Convert.ToHexString((encoding ?? Encoding.Default).GetBytes(value));
但这种方式的内存分配不够理想,会产生三次内存分配:
ToCharArray()会分配新的char[]GetBytes会分配新的byte[]ToHexString会分配新的string
ReadOnlySpan的理想与现实
引入ReadOnlySpan<T>本可以减少一次内存分配,而且由于string和char[]都能隐式转换为ReadOnlySpan<char>,还能减少方法重载数量,提升代码可维护性。理想的实现代码如下:
public static string ToHexString(ReadOnlySpan<char> value, Encoding? encoding = null) => Convert.ToHexString((encoding ?? Encoding.Default).GetBytes(value));
但讽刺的是,Encoding.GetBytes并没有接收ReadOnlySpan<char>的重载,所以实际实现时不得不调用value.ToArray()重新引入内存分配:
public static string ToHexString(ReadOnlySpan<char> value, Encoding? encoding = null) => Convert.ToHexString((encoding ?? Encoding.Default).GetBytes(value.ToArray()));
当前低分配的实现方式
目前要减少内存分配,唯一的办法还是分别实现两个重载:
public static string ToHexString(string value, Encoding? encoding = null) => Convert.ToHexString((encoding ?? Encoding.Default).GetBytes(value)); public static string ToHexString(char[] value, Encoding? encoding = null) => Convert.ToHexString((encoding ?? Encoding.Default).GetBytes(value));
这种方式的内存分配更少,但需要维护两个几乎相同的方法,可维护性有所降低,API的灵活性也稍差。
核心疑问
那么问题来了:
- 何时应该将
ReadOnlySpan<T>作为方法参数,而非使用显式类型或重载? - 开发时应该优先偏向可维护性、基于隐式转换的简洁API,还是优先考虑性能?
- 是否有相关的官方指导原则可以遵循?
内容的提问来源于stack exchange,提问作者Matthew Layton
相关产品推荐
相关产品推荐

