.NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例

栏目: ASP.NET · 发布时间: 5年前

内容简介:我们知道,在编译期间相同的字符串,在运行期间就会是相同的字符串实例。然而,如果编译期间存在字符串的运算,那么在运行期间是否是同一个实例呢?只要编译期间能够完全确定的字符串,就会是同一个实例。字符串在编译期间能确定的运算包括:

我们知道,在编译期间相同的字符串,在运行期间就会是相同的字符串实例。然而,如果编译期间存在字符串的运算,那么在运行期间是否是同一个实例呢?

只要编译期间能够完全确定的字符串,就会是同一个实例。

字符串在编译期间能确定的运算包括:

A + B
$"{A}"

字符串拼接

对于拼接,我们不需要运行便能知道是否是同一个实例:

private const string X = "walterlv is a";
private const string Y = "逗比";
private const string Z = X + Y;

以上这段代码是可以编译通过的,因为能够写为 const 的字符串,一定是编译期间能够确定的。

字符串内插

对于字符串内插,以上代码我们不能写成 const

.NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例

错误提示为:常量的初始化必须使用编译期间能够确定的常量。

然而,这段代码不能在编译期间确定吗?实际上我们有理由认为编译器其实是能够确定的,只是编译器这个阶段没有这么去做而已。

但是,我们写一个程序来验证这是否是同一个实例:

using System;

namespace Walterlv.Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(ReferenceEquals(A, A));
            Console.WriteLine(ReferenceEquals(C, C));
            Console.WriteLine(ReferenceEquals(E, E));
            Console.WriteLine(ReferenceEquals(G, G));
            Console.ReadKey(true);
        }

        private static string A => $"walterlv is a {B}";
        private static string B => "逗比";
        private static string C => $"walterlv is a {D}";
        private static string D = "逗比";
        private static string E => $"walterlv is a {F}";
        private static readonly string F = "逗比";
        private static string G => $"walterlv is a {H}";
        private const string H = "逗比";
    }
}

以上代码的输出为:

False
False
False
True

也就是说,对于最后一种情况,也就是内插的字符串是常量的时候,得到的字符串是同一个实例;这能间接证明编译期间完全确定了字符串 G。

注意,其他情况都不能完全确定:

  1. 属性内插时一定不确定;
  2. 静态字段内插时,无论是否是只读的,都不能确定。(谁知道有没有人去反射改掉呢?)

我们可以通过 IL 来确定前面的间接证明(代码太长,我只贴出来最重要的 G 字符串,以及一个用来比较的 E 字符串):

.method private hidebysig static specialname string
    get_G() cil managed
{
    .maxstack 8

    // [22 36 - 22 56]
    IL_0000: ldstr        "walterlv is a 逗比"
    IL_0005: ret

}
.method private hidebysig static specialname string
    get_E() cil managed
{
    .maxstack 8

    // [20 36 - 20 56]
    IL_0000: ldstr        "walterlv is a "
    IL_0005: ldsfld       string Walterlv.Demo.Roslyn.Program::F
    IL_000a: call         string [System.Runtime]System.String::Concat(string, string)
    IL_000f: ret

}

可以发现,实际上 G 已经在编译期间完全确定了。

扩展:修改编译期间的字符串

前面我们说到可以在编译期间完全确定的字符串。呃,为什么一定要抬杠额外写一节呢?

下面我们修改编译期间确定的字符串,看看会发生什么:

static unsafe void Main(string[] args)
{
    // 这里的 G 就是前面定义的那个 G。
    Console.WriteLine("walterlv is a 逗比");
    Console.WriteLine(G);
    fixed (char* ptr = "walterlv is a 逗比")
    {
        *ptr = 'W';
    }
    Console.WriteLine("walterlv is a 逗比");
    Console.WriteLine(G);

    Console.ReadKey(true);
}

运行结果是:

walterlv is a 逗比
walterlv is a 逗比
Walterlv is a 逗比
Walterlv is a 逗比

虽然我们看起来只是在修改我们自己局部定义的一个字符串,但是实际上已经修改了另一个常量以及属性 G。

少年,使用指针修改字符串是很危险的!鬼知道你会把程序改成什么样!

参考资料

本文会经常更新,请阅读原文: https://walterlv.com/post/same-strings-at-compile-time-are-the-same-instances-at-runtime.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

.NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接:https://walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 (walter.lv@qq.com)


以上所述就是小编给大家介绍的《.NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

High Performance Python

High Performance Python

Micha Gorelick、Ian Ozsvald / O'Reilly Media / 2014-9-10 / USD 39.99

If you're an experienced Python programmer, High Performance Python will guide you through the various routes of code optimization. You'll learn how to use smarter algorithms and leverage peripheral t......一起来看看 《High Performance Python》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器