内容简介:我们知道,在编译期间相同的字符串,在运行期间就会是相同的字符串实例。然而,如果编译期间存在字符串的运算,那么在运行期间是否是同一个实例呢?只要编译期间能够完全确定的字符串,就会是同一个实例。字符串在编译期间能确定的运算包括:
我们知道,在编译期间相同的字符串,在运行期间就会是相同的字符串实例。然而,如果编译期间存在字符串的运算,那么在运行期间是否是同一个实例呢?
只要编译期间能够完全确定的字符串,就会是同一个实例。
字符串在编译期间能确定的运算包括:
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中检查字符串是否包含子字符串?
- 字符串、字符处理总结
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
scikit learn机器学习
黄永昌 / 机械工业出版社 / 2018-3-1 / CNY 59.00
本书通过通俗易懂的语言、丰富的图示和生动的实例,拨开了笼罩在机器学习上方复杂的数学“乌云”,让读者以较低的代价和门槛轻松入门机器学习。本书共分为11章,主要介绍了在Python环境下学习scikit-learn机器学习框架的相关知识。本书涵盖的主要内容有机器学习概述、Python机器学习软件包、机器学习理论基础、k-近邻算法、线性回归算法、逻辑回归算法、决策树、支持向量机、朴素贝叶斯算法、PCA ......一起来看看 《scikit learn机器学习》 这本书的介绍吧!