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