连接ReadOnlySpan

问题描述 投票:1回答:3

好的,.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?
c# .net-core .net-core-2.1
3个回答
1
投票

我周五下午快速的想法:

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


2
投票

你可以使用像这样的缓冲区实现这一点=>

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

1
投票

以下是.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()}";
© www.soinside.com 2019 - 2024. All rights reserved.