我刚刚在c# 2.0中写了一个字符串反向函数(即LINQ不可用),然后想到了这个:

public string Reverse(string text)
{
    char[] cArray = text.ToCharArray();
    string reverse = String.Empty;
    for (int i = cArray.Length - 1; i > -1; i--)
    {
        reverse += cArray[i];
    }
    return reverse;
}

就我个人而言,我并不喜欢这个功能,我相信有更好的方法来实现它。是吗?


当前回答

简单而漂亮的答案是使用扩展方法:

static class ExtentionMethodCollection
{
    public static string Inverse(this string @base)
    {
        return new string(@base.Reverse().ToArray());
    }
}

这是输出:

string Answer = "12345".Inverse(); // = "54321"

其他回答

static void Main(string[] args)
{
    string str = "";
    string reverse = "";
    Console.WriteLine("Enter the value to reverse");
    str = Console.ReadLine();
    int length = 0;
    length = str.Length - 1;
    while(length >= 0)
    {
        reverse = reverse + str[length];
        length--;
    }
    Console.Write("Reverse string is {0}", reverse);
    Console.ReadKey();
}

首先,你不需要调用ToCharArray,因为字符串已经可以被索引为char数组,所以这将节省你的分配。

下一个优化是使用StringBuilder来防止不必要的分配(因为字符串是不可变的,连接它们每次都会复制一个字符串)。为了进一步优化这一点,我们预先设置了StringBuilder的长度,这样它就不需要扩展它的缓冲区。

public string Reverse(string text)
{
    if (string.IsNullOrEmpty(text))
    {
        return text;
    }

    StringBuilder builder = new StringBuilder(text.Length);
    for (int i = text.Length - 1; i >= 0; i--)
    {
        builder.Append(text[i]);
    }

    return builder.ToString();
}

编辑:性能数据

我使用Array测试了这个函数和这个函数。用下面的简单程序进行反向,其中Reverse1是一个函数,Reverse2是另一个函数:

static void Main(string[] args)
{
    var text = "abcdefghijklmnopqrstuvwxyz";

    // pre-jit
    text = Reverse1(text); 
    text = Reverse2(text);

    // test
    var timer1 = Stopwatch.StartNew();
    for (var i = 0; i < 10000000; i++)
    {
        text = Reverse1(text);
    }

    timer1.Stop();
    Console.WriteLine("First: {0}", timer1.ElapsedMilliseconds);

    var timer2 = Stopwatch.StartNew();
    for (var i = 0; i < 10000000; i++)
    {
        text = Reverse2(text);
    }

    timer2.Stop();
    Console.WriteLine("Second: {0}", timer2.ElapsedMilliseconds);

    Console.ReadLine();
}

事实证明,对于短字符串数组。反向方法大约是上面方法的两倍,对于更长的字符串,差异甚至更明显。已知这个数组。反向法既简单又快,我建议你用那个而不是这个。我把这个留在这里只是为了表明这不是你应该做的方式(让我很惊讶!)

如果字符串只包含ASCII字符,则可以使用此方法。

    public static string ASCIIReverse(string s)
    {
        byte[] reversed = new byte[s.Length];

        int k = 0;
        for (int i = s.Length - 1; i >= 0; i--)
        {
            reversed[k++] = (byte)s[i];
        }

        return Encoding.ASCII.GetString(reversed);
    }

选择倒档(“somestring”); 完成了。

从。net Core 2.1开始,有一种新的方法可以使用字符串反转字符串。创建方法。

请注意,这个解决方案不能正确处理Unicode组合字符等,因为“Les Mise\u0301rables”将被转换为“selbarésiM seL”。请参阅其他答案以获得更好的解决方案。

public static string Reverse(string input)
{
    return string.Create<string>(input.Length, input, (chars, state) =>
    {
        state.AsSpan().CopyTo(chars);
        chars.Reverse();
    });
}

这实际上是将输入的字符复制到一个新字符串,并在适当的位置反转新字符串。

为什么是弦。创建有用吗?

当从现有数组创建字符串时,将分配一个新的内部数组并复制其值。否则,就有可能在创建字符串之后(在安全的环境中)对其进行变异。也就是说,在下面的代码片段中,我们必须分配一个长度为10的数组两次,一次作为缓冲区,一次作为字符串的内部数组。

var chars = new char[10];
// set array values
var str = new string(chars);

字符串。Create本质上允许我们在字符串创建期间操作内部数组。也就是说,我们不再需要缓冲区,因此可以避免分配一个字符数组。

史蒂夫·戈登在这里写了更详细的内容。在MSDN上也有一篇文章。

如何使用string.Create?

public static string Create<TState>(int length, TState state, SpanAction<char, TState> action);

该方法有三个参数:

要创建的字符串长度, 你想用来动态创建新字符串的数据, 以及从数据创建最终字符串的委托,其中第一个参数指向新字符串的内部char数组,第二个参数是传递给string. create的数据(状态)。

在委托内部,我们可以指定如何从数据中创建新字符串。在本例中,我们只是将输入字符串的字符复制到新字符串使用的Span。然后我们反转Span,因此整个字符串都反转了。

基准

为了比较我提出的反转字符串的方法与已接受的答案,我使用BenchmarkDotNet编写了两个基准测试。

public class StringExtensions
{
    public static string ReverseWithArray(string input)
    {
        var charArray = input.ToCharArray();
        Array.Reverse(charArray);
        return new string(charArray);
    }

    public static string ReverseWithStringCreate(string input)
    {
        return string.Create(input.Length, input, (chars, state) =>
        {
            state.AsSpan().CopyTo(chars);
            chars.Reverse();
        });
    }
}

[MemoryDiagnoser]
public class StringReverseBenchmarks
{
    private string input;

    [Params(10, 100, 1000)]
    public int InputLength { get; set; }


    [GlobalSetup]
    public void SetInput()
    {
        // Creates a random string of the given length
        this.input = RandomStringGenerator.GetString(InputLength);
    }

    [Benchmark(Baseline = true)]
    public string WithReverseArray() => StringExtensions.ReverseWithArray(input);

    [Benchmark]
    public string WithStringCreate() => StringExtensions.ReverseWithStringCreate(input);
}

下面是我的测试结果:

| Method           | InputLength |         Mean |      Error |    StdDev |  Gen 0 | Allocated |
| ---------------- | ----------- | -----------: | ---------: | --------: | -----: | --------: |
| WithReverseArray | 10          |    45.464 ns |  0.4836 ns | 0.4524 ns | 0.0610 |      96 B |
| WithStringCreate | 10          |    39.749 ns |  0.3206 ns | 0.2842 ns | 0.0305 |      48 B |
|                  |             |              |            |           |        |           |
| WithReverseArray | 100         |   175.162 ns |  2.8766 ns | 2.2458 ns | 0.2897 |     456 B |
| WithStringCreate | 100         |   125.284 ns |  2.4657 ns | 2.0590 ns | 0.1473 |     232 B |
|                  |             |              |            |           |        |           |
| WithReverseArray | 1000        | 1,523.544 ns |  9.8808 ns | 8.7591 ns | 2.5768 |    4056 B |
| WithStringCreate | 1000        | 1,078.957 ns | 10.2948 ns | 9.6298 ns | 1.2894 |    2032 B |

正如你所看到的,使用ReverseWithStringCreate,我们只分配了ReverseWithArray方法使用的一半内存。