我有一个带有文本框的DetailsView 我希望输入的数据总是以大写的第一个字母保存。

例子:

"red" --> "Red"
"red house" --> " Red house"

我怎样才能实现性能最大化呢?


注意:

Based on the answers and the comments under the answers, many people think this is asking about capitalizing all words in the string. E.g. => Red House It isn't, but if that is what you seek, look for one of the answers that uses TextInfo's ToTitleCase method. (Note: Those answers are incorrect for the question actually asked.) See TextInfo.ToTitleCase documentation for caveats (doesn't touch all-caps words - they are considered acronyms; may lowercase letters in middle of words that "shouldn't" be lowered, e.g., "McDonald" → "Mcdonald"; not guaranteed to handle all culture-specific subtleties re capitalization rules.)


注意:

第一个字母之后的字母是否必须小写,这个问题很模糊。公认的答案假定只有第一个字母需要修改。如果要强制字符串中除第一个字母外的所有字母都小写,请查找包含ToLower且不包含ToTitleCase的答案。


当前回答

使用string.Create()并在方法中避免使用throw关键字(是的,您没有看错),我们可以进一步采用Marcell的答案。此外,我的方法处理任意长度的字符串(例如,几兆字节的文本)。

public static string L33t(this string s)
{
    static void ThrowError() => throw new ArgumentException("There is no first letter");

    if (string.IsNullOrEmpty(s))
        ThrowError();                      // No "throw" keyword to avoid costly IL

    return string.Create(s.Length, s, (chars, state) =>
    {
        state.AsSpan().CopyTo(chars);      // No slicing to save some CPU cycles
        chars[0] = char.ToUpper(chars[0]);
    });
}

性能

下面是在。net Core 3.1.7 64位上运行的基准测试的数据。我添加了一个更长的字符串,以精确计算额外拷贝的成本。

Method Data Mean Error StdDev Median
L33t red 8.545 ns 0.4612 ns 1.3308 ns 8.075 ns
Marcell red 9.153 ns 0.3377 ns 0.9471 ns 8.946 ns
L33t red house 7.715 ns 0.1741 ns 0.4618 ns 7.793 ns
Marcell red house 10.537 ns 0.5002 ns 1.4351 ns 10.377 ns
L33t red r(...)house [89] 11.121 ns 0.6774 ns 1.9106 ns 10.612 ns
Marcell red r(...)house [89] 16.739 ns 0.4468 ns 1.3033 ns 16.853 ns

完整的测试代码

using System;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace CorePerformanceTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<StringUpperTest>();
        }
    }

    public class StringUpperTest
    {
        [Params("red", "red house", "red red red red red red red red red red red red red red red red red red red red red house")]
        public string Data;

        [Benchmark]
        public string Marcell() => Data.Marcell();

        [Benchmark]
        public string L33t() => Data.L33t();
    }

    internal static class StringExtensions
    {
        public static string Marcell(this string s)
        {
            if (string.IsNullOrEmpty(s))
                throw new ArgumentException("There is no first letter");

            Span<char> a = stackalloc char[s.Length];
            s.AsSpan(1).CopyTo(a.Slice(1));
            a[0] = char.ToUpper(s[0]);
            return new string(a);
        }

        public static string L33t(this string s)
        {
            static void ThrowError() => throw new ArgumentException("There is no first letter");

            if (string.IsNullOrEmpty(s))
                ThrowError(); // IMPORTANT: Do not "throw" here!

            return string.Create(s.Length, s, (chars, state) =>
            {
                state.AsSpan().CopyTo(chars);
                chars[0] = char.ToUpper(chars[0]);
            });
        }
    }
}

如果你能让它更快,请告诉我!

其他回答

string input = "red HOUSE";
System.Text.StringBuilder sb = new System.Text.StringBuilder(input);

for (int j = 0; j < sb.Length; j++)
{
    if ( j == 0 ) //catches just the first letter
        sb[j] = System.Char.ToUpper(sb[j]);
    else  //everything else is lower case
        sb[j] = System.Char.ToLower(sb[j]);
}
// Store the new string.
string corrected = sb.ToString();
System.Console.WriteLine(corrected);

不同c#版本的解决方案

c# 8,至少。net Core 3.0或。net Standard 2.1

public static class StringExtensions
{
    public static string FirstCharToUpper(this string input) =>
        input switch
        {
            null => throw new ArgumentNullException(nameof(input)),
            "" => throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)),
            _ => string.Concat(input[0].ToString().ToUpper(), input.AsSpan(1))
        };
}

自从。net Core 3.0 / . net Standard 2.1以来,String.Concat()支持ReadonlySpan<char>,如果我们使用. asspan(1)而不是. substring(1),就节省了一个分配。

C# 8

public static class StringExtensions
{
    public static string FirstCharToUpper(this string input) =>
        input switch
        {
            null => throw new ArgumentNullException(nameof(input)),
            "" => throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)),
            _ => input[0].ToString().ToUpper() + input.Substring(1)
        };
}

C# 7

public static class StringExtensions
{
    public static string FirstCharToUpper(this string input)
    {
        switch (input)
        {
            case null: throw new ArgumentNullException(nameof(input));
            case "": throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input));
            default: return input[0].ToString().ToUpper() + input.Substring(1);
        }
    }
}

非常古老的答案

public static string FirstCharToUpper(string input)
{
    if (String.IsNullOrEmpty(input))
        throw new ArgumentException("ARGH!");
    return input.First().ToString().ToUpper() + String.Join("", input.Skip(1));
}

这个版本比较短。为了更快地解决问题,请看迭戈的回答。

public static string FirstCharToUpper(string input)
{
    if (String.IsNullOrEmpty(input))
        throw new ArgumentException("ARGH!");
    return input.First().ToString().ToUpper() + input.Substring(1);
}

可能最快的解决方案是Darren的(甚至有一个基准测试),尽管我会改变它的string.IsNullOrEmpty(s)验证来抛出异常,因为最初的需求期望第一个字母存在,以便它可以大写。注意,这段代码适用于一般字符串,而不是文本框中的有效值。

我在c#大写首字母- Dot Net Perls中找到了一些东西:

static string UppercaseFirst(string s)
{
    // Check for empty string.
    if (string.IsNullOrEmpty(s))
    {
        return string.Empty;
    }

    // Return char and concat substring.
    return char.ToUpper(s[0]) + s.Substring(1);
}

因为我碰巧也在做这方面的工作,并且四处寻找任何想法,这就是我想到的解决方案。它使用LINQ,并且能够将字符串的第一个字母大写,即使第一个字母不是字母。这是我最后做的扩展方法。

public static string CaptalizeFirstLetter(this string data)
{
    var chars = data.ToCharArray();

    // Find the Index of the first letter
    var charac = data.First(char.IsLetter);
    var i = data.IndexOf(charac);

    // capitalize that letter
    chars[i] = char.ToUpper(chars[i]);

    return new string(chars);
}

我相信有一种方法可以优化或者清理一下。

我用这个来纠正名字。它的基本原理是,如果字符遵循特定的模式,则将其更改为大写。在这种情况下,我选择了空格和“Mc”的破折号。

private String CorrectName(String name)
{
    List<String> StringsToCapitalizeAfter = new List<String>() { " ", "-", "Mc" };
    StringBuilder NameBuilder = new StringBuilder();
    name.Select(c => c.ToString()).ToList().ForEach(c =>
    {
        c = c.ToLower();
        StringsToCapitalizeAfter.ForEach(s =>
        {
            if(String.IsNullOrEmpty(NameBuilder.ToString()) ||
               NameBuilder.ToString().EndsWith(s))
            {
                c = c.ToUpper();
            }
        });
        NameBuilder.Append(c);
    });
    return NameBuilder.ToString();
}