我有一个带有文本框的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]);
});
}
}
}
如果你能让它更快,请告诉我!
我想提供一个“最大性能”的答案。在我看来,“最大性能”的答案抓住了所有的场景,并提供了解释这些场景的问题的答案。所以,这是我的答案。原因如下:
IsNullOrWhiteSpace accounts for strings that are just spaces or null/empty.
.Trim() removes white space from the front and back of the string.
.First() takes the first element of an IEnumerable<TSource> (or string).
We should check to see if it is a letter that can/should be uppercase.
We then add the rest of the string, only if the length indicates we should.
By .NET best practice, we should provide a culture under System.Globalization.CultureInfo.
Providing them as optional parameters makes this method totally reusable, without having to type the chosen culture every time.
I also noticed that my and most of these answers did not maintain the whitespace at the beginning of the string. This will also show how to maintain that whitespace.
//Capitalize the first letter disregard all chars using regex.
public static string RegCapString(this string instring, string culture = "en-US", bool useSystem = false)
{
if (string.IsNullOrWhiteSpace(instring))
{
return instring;
}
var m = Regex.Match(instring, "[A-Za-z]").Index;
return instring.Substring(0, m) + instring[m].ToString().ToUpper(new CultureInfo(culture, useSystem)) + instring.Substring(m + 1);
}
//Capitalize first char if it is a letter disregard white space.
public static string CapString(this string instring, string culture = "en-US", bool useSystem = false)
{
if (string.IsNullOrWhiteSpace(instring) ||
!char.IsLetter(instring.Trim().First()))
{
return instring;
}
var whiteSpaces = instring.Length - instring.TrimStart().Length;
return (new string(' ', whiteSpaces)) +
instring.Trim().First().ToString().ToUpper(new CultureInfo(culture, useSystem)) +
((instring.TrimStart().Length > 1) ? instring.Substring(whiteSpaces + 1) : "");
}
由于这个问题是关于最大化性能的,我采用了Darren的版本来使用跨度,这减少了垃圾,并提高了大约10%的速度。
/// <summary>
/// Returns the input string with the first character converted to uppercase
/// </summary>
public static string ToUpperFirst(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);
}
性能
Method |
Data |
Mean |
Error |
StdDev |
Carlos |
red |
107.29 ns |
2.2401 ns |
3.9234 ns |
Darren |
red |
30.93 ns |
0.9228 ns |
0.8632 ns |
Marcell |
red |
26.99 ns |
0.3902 ns |
0.3459 ns |
Carlos |
red house |
106.78 ns |
1.9713 ns |
1.8439 ns |
Darren |
red house |
32.49 ns |
0.4253 ns |
0.3978 ns |
Marcell |
red house |
27.37 ns |
0.3888 ns |
0.3637 ns |
完整的测试代码
using System;
using System.Linq;
using BenchmarkDotNet.Attributes;
namespace CorePerformanceTest
{
public class StringUpperTest
{
[Params("red", "red house")]
public string Data;
[Benchmark]
public string Carlos() => Data.Carlos();
[Benchmark]
public string Darren() => Data.Darren();
[Benchmark]
public string Marcell() => Data.Marcell();
}
internal static class StringExtensions
{
public static string Carlos(this string input) =>
input switch
{
null => throw new ArgumentNullException(nameof(input)),
"" => throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)),
_ => input.First().ToString().ToUpper() + input.Substring(1)
};
public static string Darren(this string s)
{
if (string.IsNullOrEmpty(s))
throw new ArgumentException("There is no first letter");
char[] a = s.ToCharArray();
a[0] = char.ToUpper(a[0]);
return new string(a);
}
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);
}
}
}