内容简介:我们知道,在编译期间相同的字符串,在运行期间就会是相同的字符串实例。然而,如果编译期间存在字符串的运算,那么在运行期间是否是同一个实例呢?只要编译期间能够完全确定的字符串,就会是同一个实例。字符串在编译期间能确定的运算包括:
我们知道,在编译期间相同的字符串,在运行期间就会是相同的字符串实例。然而,如果编译期间存在字符串的运算,那么在运行期间是否是同一个实例呢?
只要编译期间能够完全确定的字符串,就会是同一个实例。
字符串在编译期间能确定的运算包括:
A + B
$"{A}"
字符串拼接
对于拼接,我们不需要运行便能知道是否是同一个实例:
private const string X = "walterlv is a"; private const string Y = "逗比"; private const string Z = X + Y;
以上这段代码是可以编译通过的,因为能够写为 const 的字符串,一定是编译期间能够确定的。
字符串内插
对于字符串内插,以上代码我们不能写成 const :
错误提示为:常量的初始化必须使用编译期间能够确定的常量。
然而,这段代码不能在编译期间确定吗?实际上我们有理由认为编译器其实是能够确定的,只是编译器这个阶段没有这么去做而已。
但是,我们写一个程序来验证这是否是同一个实例:
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。
注意,其他情况都不能完全确定:
- 属性内插时一定不确定;
- 静态字段内插时,无论是否是只读的,都不能确定。(谁知道有没有人去反射改掉呢?)
我们可以通过 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 ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接:https://walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 (walter.lv@qq.com) 。
以上所述就是小编给大家介绍的《.NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
- 查找一个字符串中最长不含重复字符的子字符串,计算该最长子字符串的长度
- (三)C语言之字符串与字符串函数
- 算法笔记字符串处理问题H:编排字符串(2064)
- 如何在JavaScript中检查字符串是否包含子字符串?
- 字符串、字符处理总结
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
WWW信息体系结构(影印版第2版)
Louis Rosenfeld / 清华大学出版社 / 2003-6 / 49.8
如今的网站和内联网已经变得比以前越来越大,越来越有价值,而且越来越复杂,同时其用户也变得更忙,也更加不能容忍错误的发生。数目庞大的信息、快速的变化、新兴的技术和公司策略是设计师、信息体系结构构建师和网站管理员必须面对的事情,而这些已经让某些网让看起来像是个快速增长却规划很差的城市——到处都是路,却无法导航。规划精良的信息体系结构当前正是最关键性的。 本书介绍的是如何使用美学和机械学的理念创建......一起来看看 《WWW信息体系结构(影印版第2版)》 这本书的介绍吧!