好的,.NET Core 2.1已经登陆了。有了它,我们有了一种新的方法来处理字符串数据是ReadOnlySpan<char>
。它非常适合拆分字符串数据,但将跨度组合在一起呢?
var hello = "Hello".AsSpan();
var space = " ".AsSpan();
var world = "World".AsSpan();
var result = ...; // How do I get "Hello World" out of the 3 above?
我周五下午快速的想法:
var hello = "Hello";
var helloS = hello.AsSpan();
var spaceS = " ".AsSpan();
var worldS = "World".AsSpan();
var sentence = helloS.ToString() + spaceS.ToString() + worldS.ToString();
//Gives "Hello World"
至少根据我在LinqPad上的快速播放,以及快速阅读System.Memory Source
你可以使用像这样的缓冲区实现这一点=>
var hello = "Hello".AsSpan();
var space = " ".AsSpan();
var world = "World".AsSpan();
// First allocate the buffer with the target size
char[] buffer = new char[hello.Length + space.Length + world.Length];
// "Convert" it to writable Span<char>
var span = new Span<char>(buffer);
// Then copy each span at the right position in the buffer
int index = 0;
hello.CopyTo(span.Slice(index, hello.Length));
index += hello.Length;
space.CopyTo(span.Slice(index, space.Length));
index += space.Length;
world.CopyTo(span.Slice(index, world.Length));
// Finality get back the string
string result = span.ToString();
您可以使用arraypool重新优化缓冲区
char[] buffer = ArrayPool<char>.Shared.Rent(hello.Length + space.Length + world.Length);
// ...
ArrayPool<char>.Shared.Return(buffer);
以下是.NET团队在内部为Path.Join处理此问题的示例:
private static unsafe string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second)
{
Debug.Assert(first.Length > 0 && second.Length > 0, "should have dealt with empty paths");
bool hasSeparator = PathInternal.IsDirectorySeparator(first[first.Length - 1])
|| PathInternal.IsDirectorySeparator(second[0]);
fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second))
{
return string.Create(
first.Length + second.Length + (hasSeparator ? 0 : 1),
(First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, HasSeparator: hasSeparator),
(destination, state) =>
{
new Span<char>((char*)state.First, state.FirstLength).CopyTo(destination);
if (!state.HasSeparator)
destination[state.FirstLength] = PathInternal.DirectorySeparatorChar;
new Span<char>((char*)state.Second, state.SecondLength).CopyTo(destination.Slice(state.FirstLength + (state.HasSeparator ? 0 : 1)));
});
}
}
如果您想避免使用unsafe
并使用可能更容易阅读的内容,您可以使用以下内容:
public static ReadOnlySpan<char> Concat(this ReadOnlySpan<char> first, ReadOnlySpan<char> second)
{
return new string(first.ToArray().Concat(second.ToArray()).ToArray()).AsSpan();
}
public static ReadOnlySpan<char> Concat(this string first, ReadOnlySpan<char> second)
{
return new string(first.ToArray().Concat(second.ToArray()).ToArray()).ToArray();
}
使用ReadOnlySpan
是相当低的水平,并针对速度进行了优化,因此你如何做到这可能取决于你自己的情况。但在许多情况下,回到string
插值和StringBuilder
(或根本不转换为ReadOnlySpan
)可能很好。所以
var sb = new StringBuilder();
return sb
.Append(hello)
.Append(space)
.Append(world)
.ToString();
要么
return $"{hello.ToString()}{space.ToString()}{world.ToString()}";