首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >.NET6新东西--插值字符串优化

.NET6新东西--插值字符串优化

作者头像
喵叔
发布2021-11-26 14:15:17
发布2021-11-26 14:15:17
1.5K0
举报
文章被收录于专栏:喵叔's 专栏喵叔's 专栏

字符串是我们平时使用最多的一个类型,从C#6开始就支持插值字符串,方便我们进行字符串的操作,并且大部分分析器也推荐使用插值这种写法,因为它够使得我们的代码更加清晰简洁,到了.NET6中的C#10则为我们提供了更好的实现方式以及更佳的性能。 那么什么是插值字符串呢?它是以符开头的,类似于 “Hello {name}” 这样的字符串,下面的例子是插值字符串的简单使用:

代码语言:javascript
复制
var name = "插值字符串";
var hello = $"你好 {name}!";
var num= 10;
var numMessage= $"我喜欢数字 {num}";

我们不需要使用format就可以直接简化字符串拼接,并且对于一些简单的字符串拼接可以简化成string.Concat,在.NET6之前的版本中它会被翻译成低版本C#中的string.Format形式,上述代码翻译成低版本C# 代码如下所示:

代码语言:javascript
复制
string name = "插值字符串";
string hello = string.Concat("你好 ", name, "!");
int num= 10;
string numMessage= string.Format("我喜欢数字 {0}", );

对于string.Format来说,如果参数是值类型会发生装箱,变为 object,这一点我们可以通过IL代码看出。这里需要注意的是插值字符串格式化的时候会使用当前的CultureInfo,如果我们需要使用不同的CultureInfo或手动指定CultureInfo,那么可以使用FormattableString或FormattableStringFactory来实现。代码如下会根据指定的CultureInfo显示出不同的数字格式:

代码语言:javascript
复制
var id=35000;
FormattableString str1 = $"id是{id}";
Console.WriteLine(str1.Format);
Console.WriteLine(str1.ToString(new CultureInfo("zh-CN")));
str1 = FormattableStringFactory.Create("Hello {0}", id);
Console.WriteLine(str1.Format);
Console.WriteLine(str1.ToString(new CultureInfo("en-US")));

在.NET6 中本文的第一段代码会翻译成生成下面这样的:

代码语言:javascript
复制
string name = "插值字符串";
string hello = string.Concat ("Hello ", name, "!");
int num= 10;
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(11, 1);
defaultInterpolatedStringHandler.AppendLiteral("我喜欢数字 ");
defaultInterpolatedStringHandler.AppendFormatted(num);
string numDesc = defaultInterpolatedStringHandler.ToStringAndClear();

在.NET6中会由DefaultInterpolatedStringHandler处理插值字符串。它DefaultInterpolatedStringHandler是结构体,并且包含泛型方法AppendFormatted来避免装箱操作,这样它在format的时候性能更好。并且在.NET6中String增加了两个方法来支持使用新的插值处理方式,新增的方法代码如下所示:

代码语言:javascript
复制
/// Creates a new string by using the specified provider to control the formatting of the specified interpolated string.
/// An object that supplies culture-specific formatting information.
/// The interpolated string.
/// The string that results for formatting the interpolated string using the specified format provider.
public static string Create(IFormatProvider? provider, [InterpolatedStringHandlerArgument("provider")] ref DefaultInterpolatedStringHandler handler) => handler.ToStringAndClear();

/// Creates a new string by using the specified provider to control the formatting of the specified interpolated string.
/// An object that supplies culture-specific formatting information.
/// The initial buffer that may be used as temporary space as part of the formatting operation. The contents of this buffer may be overwritten.
/// The interpolated string.
/// The string that results for formatting the interpolated string using the specified format provider.
public static string Create(IFormatProvider? provider, Span<char> initialBuffer, [InterpolatedStringHandlerArgument("provider", "initialBuffer")] ref DefaultInterpolatedStringHandler handler) => handler.ToStringAndClear();

下面我们来实现一个简单的插值字符串处理器,实现一个最基本的插值字符串处理器需要满足以下四个条件:

  1. 构造函数至少需要两个int参数,一个是字符串中常量字符的长度,一个是需要格式化的参数的数量;
  2. 需要具有public的AppendLiteral(string s)方法处理常量字符的拼接;
  3. 需要具有public的AppendFormatted(T t)方法处理参数;
  4. 自定义处理器需要使用InterpolatedStringHandler标记,并且处理器可以是class也可以是struct。下面的代码就实现了一个简单的插值字符串处理器
代码语言:javascript
复制
[InterpolatedStringHandler]
public struct CustomInterpolatedStringHandler
{
    private readonly StringBuilder builder;
    public CustomInterpolatedStringHandler(int literalLength, int formattedCount)
    {
        builder = new StringBuilder(literalLength);
    }
    public void AppendLiteral(string s)
    {
        builder.Append(s);
    }
    public void AppendFormatted<T>(T t)
    {
        builder.Append(t?.ToString());
    }
    public override string ToString()
    {
        return builder.ToString();
    }
}

当我们使用它的时候,可以这么用:

代码语言:javascript
复制
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());
}
LogInterpolatedString("我喜欢的数字是10");
int num=20;
LogInterpolatedString($"我喜欢的数字是{num}");

输出结果如下:

代码语言:javascript
复制
LogInterpolatedString
我喜欢的数字是10
LogInterpolatedString
CustomInterpolatedStringHandler
我喜欢的数字是20

我们还可以在自定义的插值字符串处理器的构造器中增加自定义参数,使用InterpolatedStringHandlerArgument来引入更多构造器参数。我们改造一下上面CustomInterpolatedStringHandler代码:

代码语言:javascript
复制
[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);
        _limit = limit;
    }
    public void AppendLiteral(string s)
    {
        builder.Append(s);
    }
    public void AppendFormatted<T>(T t)
    {
        if (t is int n && n < _limit)
        {
            return;
        }
        builder.Append(t?.ToString());
    }
    public override string ToString()
    {
        return builder.ToString();
    }
}

我们修改调用代码:

代码语言:javascript
复制
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参数则不会被拼接,下面我们再来修改调用代码:

代码语言:javascript
复制
LogInterpolatedString(10, $"我喜欢的数字是{num}");
Console.WriteLine();
LogInterpolatedString(15, $"我喜欢的数字是{num}");

输出结果如下:

代码语言:javascript
复制
LogInterpolatedString
CustomInterpolatedStringHandler with limit:10
我喜欢的数字是10

LogInterpolatedString
CustomInterpolatedStringHandler with limit:15
我喜欢的数字是

从上面输出的结果可以看出第一次打印出来了 num,第二次没有打印 num。 其实还有一个特殊的参数,我们可以在构造方法中引入一个bool类型的out参数,如果值为false则不会进行字符串的拼接,我们再次改造一下前面的代码:

代码语言:javascript
复制
public CustomInterpolatedStringHandler(int literalLength, int formattedCount, int limit, out bool shouldAppend)
{
    shouldAppend = limit < 30;
    builder = new StringBuilder(shouldAppend ? literalLength : 0);
    _limit = limit;
}

当limit参数小于30时进行字符串的拼接,否则就不输出,调用代码修改如下:

代码语言:javascript
复制
LogInterpolatedString(10, $"我喜欢的数字是 {num}");
Console.WriteLine();
LogInterpolatedString(15, $"我喜欢的数字是{num}");
Console.WriteLine();
LogInterpolatedString(30, $"我喜欢的数字是{num}");

输出结果是这样的

代码语言:javascript
复制
LogInterpolatedString
CustomInterpolatedStringHandler with limit:10
我喜欢的数字是10

LogInterpolatedString
CustomInterpolatedStringHandler with limit:15
我喜欢的数字是

LogInterpolatedString
CustomInterpolatedStringHandler with limit:30

可以看到,当limit是30 的时候,输出的是空行,没有任何内容。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/11/25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档