Конкатенация ReadOnlySpan <char> - PullRequest
0 голосов
/ 01 июня 2018

Хорошо, .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?

Ответы [ 3 ]

0 голосов
/ 01 сентября 2018

Вы можете добиться этого с помощью буфера, подобного этому =>

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

Вы можете снова оптимизировать его, используя массив, для повторного использования буфера

char[] buffer =  ArrayPool<char>.Shared.Rent(hello.Length + space.Length + world.Length);
// ...
ArrayPool<char>.Shared.Return(buffer);
0 голосов
/ 23 ноября 2018

Мои быстрые мысли в пятницу днем:

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

0 голосов
/ 27 июля 2018

Вот пример того, как команда .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()}";
...