4

字符串拼接这个隐藏大坑,我表示不服~ - gui.h

 1 year ago
source link: https://www.cnblogs.com/springhgui/p/16268681.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

字符串拼接这个隐藏大坑,我表示不服~ - gui.h - 博客园

先看写个简单的代码,看看你能不能答对

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
string v1 = null;
string v2 = null;

var v3 = v1 + v2;

Console.WriteLine();

请问上面这段代码v3的值是什么?

A:null

B:string.Empty

C:异常

请读者好好思考一下再往下看~

不墨迹,直接运行代码看结果:

0979896b46084e9297073d23d9526899~tplv-k3u1fbpfcp-watermark.image?


很明显答案是 B,此时你会不会有疑问:两个null相加,怎么会是""?我也有这个疑问,而且怎么都想不明白为什么~~~

将上面的代码编译后,使用ILSpy反编译工具查看IL中间语言代码看看,如下:

.method private hidebysig static 
    void '<Main>$' (
        string[] args
    ) cil managed 
{
    // Method begins at RVA 0x2050
    // Header size: 12
    // Code size: 30 (0x1e)
    .maxstack 2
    .entrypoint
    .locals init (
        [0] string v1,
        [1] string v2,
        [2] string v3
    )

    // Console.WriteLine("Hello, World!");
    IL_0000: ldstr "Hello, World!"
    IL_0005: call void [System.Console]System.Console::WriteLine(string)
    // string text = null;
    IL_000a: nop
    IL_000b: ldnull
    IL_000c: stloc.0
    // string text2 = null;
    IL_000d: ldnull
    IL_000e: stloc.1
    // string text3 = text + text2;
    IL_000f: ldloc.0
    IL_0010: ldloc.1
    //++++++++++++++++++++++注意这一行++++++++++++++++++++++++++++
    IL_0011: call string [System.Runtime]System.String::Concat(string, string)
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    IL_0016: stloc.2
    // Console.WriteLine();
    IL_0017: call void [System.Console]System.Console::WriteLine()
    // }
    IL_001c: nop
    IL_001d: ret
} // end of method Program::'<Main>$'

主要看上面用注释标记的那行

IL_0011: call string [System.Runtime]System.String::Concat(string, string)

由此可以知道我们的两个变量相加,其实底层调用的是String::Concat(string, string)方法,从github上拉取dotnet/runtime仓库源码,找到string类型的源代码Concat(string, string)方法。

public static string Concat(string? str0, string? str1)
{
    // 第一个参数为空
    if (IsNullOrEmpty(str0))
    {
        // 第二个参数也为空
        if (IsNullOrEmpty(str1))
        {
            // 返回string.Empty
            return string.Empty;
        }
        return str1;
    }

    if (IsNullOrEmpty(str1))
    {
        return str0;
    }

    int str0Length = str0.Length;

    string result = FastAllocateString(str0Length + str1.Length);

    FillStringChecked(result, 0, str0);
    FillStringChecked(result, str0Length, str1);

    return result;
}

源码很简单,一上来就找到了返回string.Empty的结果,至此我们知道它为什么结果是string.Empty

之所以写本文,确实是实际项目中因为两个null字符串相加与我想想的不一样,出现bug,项目中的代码大概是这样的:

// context.Error和context.ErrorDes均为string类型,
// 两者绝不会存在为string.Empty的情况,但是可能同时为null
var resMsg = (context.Error + context.ErrorDes) ?? "系统异常"

本以为上面这段代码本意是想拼接两个错误信息输出,如果两个错误信息都是null,那么就返回系统异常,结果可想而知,context.Errorcontext.ErrorDes虽然均为null,但是他们的结果不是null,最终resMsg"",害~~~

虽然我们知道为啥是string.Empty了,但是还是觉得null才更加合理,不知道设计者是出于什么考虑,如果你知道,请告诉我,如果本文对你有帮助,请点赞,关注,转发,支持一波~

__EOF__

本文作者: Gui.H 本文链接: https://www.cnblogs.com/springhgui/p/16268681.html 关于博主: 评论和私信会在第一时间回复。或者直接私信我。 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处! 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK