公司查询网站查询系统,个人商城网站怎么做,广东大唐建设网站,外贸建站推广工作总结C# 10 新特性 —— 插值字符串优化Intro字符串应该是我们平时使用的最多的一个类型#xff0c;从 C# 6 开始我们开始支持了插值字符串#xff0c;使得我们可以更方便的进行字符串的操作#xff0c;现在很多分析器也推荐我们使用插值这种写法#xff0c;这能够使得我们的代码… C# 10 新特性 —— 插值字符串优化Intro字符串应该是我们平时使用的最多的一个类型从 C# 6 开始我们开始支持了插值字符串使得我们可以更方便的进行字符串的操作现在很多分析器也推荐我们使用插值这种写法这能够使得我们的代码更加清晰和简洁C# 10 提供了更好的实现方式以及更好的性能Interpolated string什么是插值字符串呢就是 $ 符号开始的类似 $Hello {name} 这样的字符串我们来看下面的示例var str $1233;
var name Alice;
var hello $Hello {name}!;
var num 10;
var numDesc $The num is {num};简单的插值字符串会简化对于不需要 format 的参数会直接简化为字符串对于一些简单的字符串拼接可以简化成 string.Concat在 C#10/.NET 6 之前的版本中其他的大多会翻译成 string.Format 的形式翻译成低版本的 C# 代码则是这样的string str 1233;
string name Alice;
string hello string.Concat(Hello , name, !);
int num 10;
string numDesc string.Format(The num is {0}, num);对于 string.Format参数如果是值类型会发生装箱变为 object我们从 IL 代码可以看得出来IL插值字符串格式化的时候会使用当前 CultureInfo如果需要使用不同 CultureInfo 或者手动指定可以借助 FormattableString/FormattableStringFactory 来实现var num 10;
FormattableString str1 $Hello {num};
Console.WriteLine(str1.Format);
Console.WriteLine(str1.ToString(new CultureInfo(zh-CN)));str1 FormattableStringFactory.Create(Hello {0}, num);
Console.WriteLine(str1.Format);
Console.WriteLine(str1.ToString(new CultureInfo(en-US)));对于 C# 10/.NET6 中则会生成下面的代码string str 1233;
string name Alice;
string hello string.Concat (Hello , name, !);
int num 10;
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler new DefaultInterpolatedStringHandler(11, 1);
defaultInterpolatedStringHandler.AppendLiteral(The num is );
defaultInterpolatedStringHandler.AppendFormatted(num);
string numDesc defaultInterpolatedStringHandler.ToStringAndClear();IL in C#10/.NET6在新版本中会由 DefaultInterpolatedStringHandler 来处理插值字符串而且这个新的 DefaultInterpolatedStringHandler 是一个结构体并且会有一个泛型方法 AppendFormattedT 来避免发生装箱在 format 的时候性能更优对于普通的字符串则使用 AppendLiteral() 方法处理声明如下namespace System.Runtime.CompilerServices
{[InterpolatedStringHandler]public ref struct DefaultInterpolatedStringHandler{public DefaultInterpolatedStringHandler(int literalLength, int formattedCount);public DefaultInterpolatedStringHandler(int literalLength, int formattedCount, System.IFormatProvider? provider);public DefaultInterpolatedStringHandler(int literalLength, int formattedCount, System.IFormatProvider? provider, System.Spanchar initialBuffer);public void AppendLiteral(string value);public void AppendFormattedT(T value);public void AppendFormattedT(T value, string? format);public void AppendFormattedT(T value, int alignment);public void AppendFormattedT(T value, int alignment, string? format);public void AppendFormatted(ReadOnlySpanchar value);public void AppendFormatted(ReadOnlySpanchar value, int alignment 0, string? format null);public void AppendFormatted(string? value);public void AppendFormatted(string? value, int alignment 0, string? format null);public void AppendFormatted(object? value, int alignment 0, string? format null);public string ToStringAndClear();}
}具体实现可以参考https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/DefaultInterpolatedStringHandler.cs在 .NET 6 中增加了两个 String 方法来支持使用新的插值处理方式/// summaryCreates a new string by using the specified provider to control the formatting of the specified interpolated string./summary
/// param nameproviderAn object that supplies culture-specific formatting information./param
/// param namehandlerThe interpolated string./param
/// returnsThe string that results for formatting the interpolated string using the specified format provider./returns
public static string Create(IFormatProvider? provider, [InterpolatedStringHandlerArgument(provider)] ref DefaultInterpolatedStringHandler handler) handler.ToStringAndClear();/// summaryCreates a new string by using the specified provider to control the formatting of the specified interpolated string./summary
/// param nameproviderAn object that supplies culture-specific formatting information./param
/// param nameinitialBufferThe initial buffer that may be used as temporary space as part of the formatting operation. The contents of this buffer may be overwritten./param
/// param namehandlerThe interpolated string./param
/// returnsThe string that results for formatting the interpolated string using the specified format provider./returns
public static string Create(IFormatProvider? provider, Spanchar initialBuffer, [InterpolatedStringHandlerArgument(provider, initialBuffer)] ref DefaultInterpolatedStringHandler handler) handler.ToStringAndClear();Custom Interpolated string handler接着我们来尝试实现一个简单的插值字符串处理器实现一个最基本的插值字符串处理器需要满足四个条件构造函数至少需要两个 int 参数一个是字符串中常量字符的长度literalLength一个是需要格式化的参数的数量(formattedCount)需要一个 public 的 AppendLiteral(string s) 方法来处理常量字符的拼接需要一个 public 的 AppendFormattedT(T t) 方法来处理参数自定义的处理器需要使用 InterpolatedStringHandler 来标记处理器可以是 class 也可以是 struct// InterpolatedStringHandlerAttribute is required for custom InterpolatedStringHandler
[InterpolatedStringHandler]
public struct CustomInterpolatedStringHandler
{// Storage for the built-up stringprivate readonly StringBuilder builder;/// summary/// CustomInterpolatedStringHandler constructor/// /summary/// param nameliteralLengthstring literal length/param/// param nameformattedCountformatted count/parampublic CustomInterpolatedStringHandler(int literalLength, int formattedCount){builder new StringBuilder(literalLength);Console.WriteLine($\tliteral length: {literalLength}, formattedCount: {formattedCount});}// Requiredpublic void AppendLiteral(string s){Console.WriteLine($\tAppendLiteral called: {{{s}}});builder.Append(s);Console.WriteLine($\tAppended the literal string);}// Requiredpublic void AppendFormattedT(T t){Console.WriteLine($\tAppendFormatted called: {{{t}}} is of type {typeof(T)});builder.Append(t?.ToString());Console.WriteLine($\tAppended the formatted object);}public override string ToString(){return builder.ToString();}
}使用示例如下private static void LogInterpolatedString(string str)
{Console.WriteLine(nameof(LogInterpolatedString));Console.WriteLine(str);
}private static void LogInterpolatedString(CustomInterpolatedStringHandler stringHandler)
{Console.WriteLine(nameof(LogInterpolatedString));Console.WriteLine(nameof(CustomInterpolatedStringHandler));Console.WriteLine(stringHandler.ToString());
}// Custom InterpolatedStringHandler
LogInterpolatedString(The num is 10);
LogInterpolatedString($The num is {num});输出结果如下LogInterpolatedString
The num is 10literal length: 11, formattedCount: 1AppendLiteral called: {The num is }Appended the literal stringAppendFormatted called: {10} is of type System.Int32Appended the formatted object
LogInterpolatedString
CustomInterpolatedStringHandler
The num is 10除此之外我们还可以在自定义的插值字符串处理器的构造器中增加自定义参数我们可以使用 InterpolatedStringHandlerArgument 来引入更多构造器参数我们在上面的示例基础上改造一下改造后 CustomInterpolatedStringHandler代码如下:[InterpolatedStringHandler]
public struct CustomInterpolatedStringHandler
{private readonly StringBuilder builder;private readonly int _limit;public CustomInterpolatedStringHandler(int literalLength, int formattedCount) : this(literalLength, formattedCount, 0){ }public CustomInterpolatedStringHandler(int literalLength, int formattedCount, int limit){builder new StringBuilder(literalLength);Console.WriteLine($\tliteral length: {literalLength}, formattedCount: {formattedCount});_limit limit;}// Requiredpublic void AppendLiteral(string s){Console.WriteLine($\tAppendLiteral called: {{{s}}});builder.Append(s);Console.WriteLine($\tAppended the literal string);}// Requiredpublic void AppendFormattedT(T t){Console.WriteLine($\tAppendFormatted called: {{{t}}} is of type {typeof(T)});if (t is int n n _limit){return;}builder.Append(t?.ToString());Console.WriteLine($\tAppended the formatted object);}public override string ToString(){return builder.ToString();}
}调用方式我们再增加一种方式以使用新引入的构造器private static void LogInterpolatedString(int limit, [InterpolatedStringHandlerArgument(limit)] ref CustomInterpolatedStringHandler stringHandler)
{Console.WriteLine(nameof(LogInterpolatedString));Console.WriteLine(${nameof(CustomInterpolatedStringHandler)} with limit:{limit});Console.WriteLine(stringHandler.ToString());
}做了一个检查如果参数是 int 并且小于传入的 limit 参数则不会被拼接来看一下下面的调用LogInterpolatedString(10, $The num is {num});
Console.WriteLine();
LogInterpolatedString(15, $The num is {num});输出结果如下literal length: 11, formattedCount: 1AppendLiteral called: {The num is }Appended the literal stringAppendFormatted called: {10} is of type System.Int32Appended the formatted object
LogInterpolatedString
CustomInterpolatedStringHandler with limit:10
The num is 10literal length: 11, formattedCount: 1AppendLiteral called: {The num is }Appended the literal stringAppendFormatted called: {10} is of type System.Int32
LogInterpolatedString
CustomInterpolatedStringHandler with limit:15
The num is从上面的结果可以看出来我们的代码是生效的第一次打印出来了 num第二次没有打印 num还有一个特殊的参数我们可以在构造方法中引入一个 bool 类型的 out 参数如果这个参数为 false 则不会进行字符串的拼接 Append我们改造一下刚才的示例示例代码如下public CustomInterpolatedStringHandler(int literalLength, int formattedCount, int limit, out bool shouldAppend)
{shouldAppend limit 20;builder new StringBuilder(shouldAppend ? literalLength : 0);Console.WriteLine($\tliteral length: {literalLength}, formattedCount: {formattedCount});_limit limit;
}当 limit 参数小于 20 时进行字符串的拼接否则就不输出测试代码如下LogInterpolatedString(10, $The num is {num});
Console.WriteLine();
LogInterpolatedString(15, $The num is {num});
Console.WriteLine();
LogInterpolatedString(20, $The num is {num});输出结果是这样的literal length: 11, formattedCount: 1AppendLiteral called: {The num is }Appended the literal stringAppendFormatted called: {10} is of type System.Int32Appended the formatted object
LogInterpolatedString
CustomInterpolatedStringHandler with limit:10
The num is 10literal length: 11, formattedCount: 1AppendLiteral called: {The num is }Appended the literal stringAppendFormatted called: {10} is of type System.Int32
LogInterpolatedString
CustomInterpolatedStringHandler with limit:15
The num isliteral length: 11, formattedCount: 1
LogInterpolatedString
CustomInterpolatedStringHandler with limit:20可以看到当 limit 是 20 的时候输出的是空行没有任何内容另外我们可以把上面的 Append 方法的返回值改成 bool如果方法中返回 false 则会造成短路类似于 ASP.NET Core 中中间件的短路后面的拼接就会取消我们再改造一下上面的示例改造一下 Append 方法public bool AppendLiteral(string s)
{if (s.Length 1)return false;Console.WriteLine($\tAppendLiteral called: {{{s}}});builder.Append(s);Console.WriteLine($\tAppended the literal string);return true;
}// Required
public bool AppendFormattedT(T t)
{Console.WriteLine($\tAppendFormatted called: {{{t}}} is of type {typeof(T)});if (t is int n n _limit){return false;}builder.Append(t?.ToString());Console.WriteLine($\tAppended the formatted object);return true;
}再来使用 LogInterpolatedString(12, $The num is {num} and the time is {DateTime.Now}!); 调用一下试一下输出结果如下literal length: 29, formattedCount: 2AppendLiteral called: {The num is }Appended the literal stringAppendFormatted called: {10} is of type System.Int32
LogInterpolatedString
CustomInterpolatedStringHandler with limit:12
The num is更多自定义可以参考默认的 DefaultInterpolatedStringHandler使用自定义的 InterpolatedStringHandler 时如果是结构体参数建议使用 ref 引用传递可以参考 https://github.com/dotnet/runtime/issues/57538More有哪些场景可以用呢下面就是一个示例更多细节可以参考https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Debug.cshttps://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Debug.cs#L280[Conditional(DEBUG)]
public static void Assert([DoesNotReturnIf(false)] bool condition, [InterpolatedStringHandlerArgument(condition)] ref AssertInterpolatedStringHandler message) Assert(condition, message.ToStringAndClear());当然不仅于此还有很多细节可以去挖掘还有 StringBuilder/Memory 等也使用了新的方式来处理插值字符串最后如果我们可以使用插值字符串就尽可能地使用插值字符串来处理从 .NET 6 以后就不会有装箱的问题了性能还会更好感兴趣的小伙伴们可以更加深入研究一下上面的示例有需要的可以从 Github 上获取 https://github.com/WeihanLi/SamplesInPractice/blob/master/CSharp10Sample/InterpolatedStringSample.csReferenceshttps://github.com/dotnet/runtime/issues/50635https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/InterpolatedStringHandlerAttribute.cshttps://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/improved-interpolated-strings.mdhttps://devblogs.microsoft.com/dotnet/string-interpolation-in-c-10-and-net-6/https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/DefaultInterpolatedStringHandler.cshttps://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated#compilation-of-interpolated-stringshttps://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.defaultinterpolatedstringhandler?viewnet-6.0https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/interpolated-string-handlerhttps://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/improved-interpolated-stringshttps://docs.microsoft.com/en-us/dotnet/csharp/tutorials/string-interpolationhttps://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Debug.cshttps://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/FormattableString.cshttps://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/FormattableStringFactory.cshttps://github.com/dotnet/runtime/issues/57538https://github.com/WeihanLi/SamplesInPractice/blob/master/CSharp10Sample/InterpolatedStringSample.cs