以我的理解,从
ReadOnlyMemory<char>
获取string
非常便宜,而从string
获取ReadOnlyMemory<char>
往往涉及分配和复制,所以不是很便宜。
幸运的是,许多过去接受
string
的 CLR 方法已经接受了接受 ReadOnlySpan<char>
的重载,这些重载可以从 ReadOnlyMemory<char>
廉价地获得,因此在许多情况下,我们可以获得 ReadOnlyMemory<char>
的性能优势,而不必替换所有我们的 string
与 ReadOnlyMemory<char>
。 (我们大多只需要记住在调用具有跨度接受重载的方法时将 string.Substring()
替换为 string.AsSpan().Slice()
。)
但是,在很多情况下
ReadOnlyMemory<char>
的性能优势无法实现。例如,如果我有一个带有 string
键的字典,我只能将 string
传递给它,这意味着当我想使用字符串的一部分作为键进行查找时,我别无选择,只能使用string.Substring()
获取查找密钥,这是昂贵的。
这种情况似乎暗示,如果整个解决方案中所有
string
实例都替换为 ReadOnlyMemory<char>
,则可以实现最大性能。如果我的字典使用 ReadOnlyMemory<char>
作为键(或者聚合 readonly struct
及其哈希码的 ReadOnlyMemory<char>
,这样哈希码就不必一直重新计算),那么我将能够使用 string
或 ReadOnlyMemory<char>
进行高效查找。
所以,我的问题:
有没有人尝试在中等规模(不是小型)解决方案中用
string
替换所有 ReadOnlyMemory<char>
?
执行此类转换时可能会遇到哪些问题?
或者我错过了一些东西,实际上可以以某种方式使用子字符串(作为字典查找等的键)而无需额外的分配?
澄清:
我不是在谈论使用
ReadOnlySpan<char>
来替换 string
,因为 string
可以存在于任何地方,而 ReadOnlySpan<char>
只能存在于堆栈中,因为它是 ref struct
。另一方面,ReadOnlyMemory<char>
是普通的struct
,而不是ref struct
,所以它可以生活在string
可以生活的所有地方。
虽然我以前没有做过这种替换,但是我能想到一个明显的问题。
string
是不可变的,这与只读不同。你必须知道字典默认依赖于 GetHashCode
方法。 string 的 GetHashCode
方法的实现是基于内容的,而 ReadOnlyMemory
是基于它引用的片段。
一个简单的反例:
var chars = "abc".ToCharArray();
var m = chars.AsMemory();
Console.WriteLine(m.GetHashCode());
chars[0] = 'n';
Console.WriteLine(m.GetHashCode()); // no change