字符串是不可变的(只读)。如果您使用的是字符串,您将始终需要一个 stackalloc 来获取该字符串的可写副本:
var str = "hello world, how are you?"
var span = stackalloc char[str.Length];
str.AsSpan().CopyTo(span);
假设您已经将消息作为跨度,它是可以在不使用另一个 stackalloc 的情况下交换 span 内的数据。在此示例中,两个跨度都是 5 个字符长。您可以一个一个地交换单个字符:
var part1 = span.Slice(0, 5);
var part2 = span.Slice(6, 5);
foreach (var i = 0; i < 5; i++)
{
var temp = part1[i];
part1[i] = part2[i];
part2[i] = temp;
}
当然,这提出了一些问题:
- 如果交换的两个跨度长度不同,会发生什么情况?
- 为什么你这么关心不做另一个stackalloc?
- 为什么不使用内置的字符串方法?
这看起来像是过早优化的情况。
编辑 1 - 实施
您发布的实现仅在两个跨度长度相同的情况下才有效。您在 cmets 中提到并非总是如此。在处理不同长度时,有几种边缘情况的组合:
// The best case - both sections are the same length,
// so you don't have to shuffle any other memory around
// It doesn't matter if there is a gap
[_, _, _, a, a, a, b, b, b, _, _, _]
[_, _, _, a, a, a, x, x, x, b, b, b, _, _, _]
// The sections are not the same length, there is no gap
[_, _, _, a, a, b, b, b, b, _, _, _]
[_, _, _, a, a, a, a, b, b, _, _, _]
// The sections are not the same length, and there is a gap
[_, _, _, a, a, x, x, x, b, b, b, b, _, _, _]
[_, _, _, a, a, a, a, x, x, x, b, b, _, _, _]
实现需要查看这些情况,并处理它们中的每一个。
public static void Swap<T>(Span<T> span, int indexA, int lengthA, int indexB, int lengthB)
{
var a = span.Slice(indexA, lengthA);
var b = span.Slice(indexB, lengthB);
if (lengthA == lengthB)
{
// The easy implementation
var temp = stackalloc T[lengthA];
a.CopyTo(temp);
b.CopyTo(a);
temp.CopyTo(b);
return;
}
var destinationA = span.Slice(indexB + lengthB - lengthA, lengthA);
var destinationB = span.Slice(indexA, lengthB);
var indexX = indexA + lengthA;
if (indexX == indexB)
{
// There is no gap between a and b
var temp = stackalloc T[lengthA];
a.CopyTo(temp);
b.CopyTo(destinationB);
temp.CopyTo(destinationA);
}
else
{
// There is a gap 'x' between 'a' and 'b' that needs to be moved too
var lengthX = indexB - indexX;
var x = span.Slice(indexX, lengthX);
var destinationX = span.Slice(indexA + lengthB, lengthX);
var tempA = stackalloc T[lengthA];
var tempX = stackalloc T[lengthX];
a.CopyTo(tempA);
x.CopyTo(tempX);
b.CopyTo(destinationB);
tempX.CopyTo(destinationX);
tempA.CopyTo(destinationA);
}
}
同样,您提到想要在不使用“stackalloc”的情况下执行此操作。为什么?您是否分析过您的代码,并发现您的交换方法存在瓶颈,特别是对“stackalloc”的调用?我想你会发现,与使用 for 循环而不是 Span.CopyTo()(可以使用 memcpy)的成本相比,这些对“stackalloc”的调用的性能可以忽略不计