詳解.Net中字符串不變性與相等判斷的特殊場(chǎng)景
今天寫(xiě)bug的時(shí)候幫同事解決了一個(gè)有趣的問(wèn)題,可能很多人都會(huì)答錯(cuò)。分享給大家。
問(wèn)題
請(qǐng)看以下例子,并回答問(wèn)題。
var s1 = "12"; var s2 = "12"; //序列化方式1 var o3 = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(Newtonsoft.Json.JsonConvert.SerializeObject(s1)); //序列化方式2 MemoryStream stream = new MemoryStream(); System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); bf.Serialize(stream, s1); stream.Seek(0, SeekOrigin.Begin); var o4 = bf.Deserialize(stream); //====分割線=================================================== var e1 = object.ReferenceEquals(s1, s2); var e2 = o4 == s1; var e3 = s1.Equals(o4); var e4 = o3 == o4; Console.ReadKey();
請(qǐng)回答分割線后e1, e2, e3, e4 值為true還是false。
人人都知道在.Net中字符串是享元模式的經(jīng)典范例。字符串具有不變性。(至少在托管層,事實(shí)上可以在非托管層修改字符串的值),但你真的能回答對(duì)上面的問(wèn)題么?
答案
e1 = true;
e2 = false;
e3 = true;
e4 = false;
要了解這個(gè)問(wèn)題首先可以看下字符串在內(nèi)存中的布局。
如何在visual studio中查看變量的內(nèi)存布局
在VS中可以非常方便的查看托管或非托管變量的內(nèi)存值。方法如下。
- 依次在調(diào)試模式下打開(kāi) 調(diào)試 -> 窗口 -> 內(nèi)存 -> 內(nèi)存1(1~4均可) 打開(kāi)內(nèi)存對(duì)話框。
- 在地址欄中輸入變量名即可。
字符串變量在內(nèi)存中的布局
在.Net中字符串是以UTF-16格式在內(nèi)存中保存的。在本例中s1的內(nèi)存如下。
00 00 00 00 00 00 00 00 98 d6 fc e5 fb 7f 00 00 02 00 00 00 31 00 32 00
這里可能與你拿到的結(jié)果不一樣。你可能并沒(méi)有前8位0x00
,因?yàn)槲野褜?duì)象頭帶上了。下面依次解釋各段含義。
00 00 00 00 00 00 00 00
最開(kāi)始的8比特是對(duì)象頭。其中,在64位下,高4位為0,低4位為一個(gè)不為0的數(shù)(這里由于并沒(méi)有執(zhí)行l(wèi)ock或Gethashcode操作,所以這里為0,感興趣的自行實(shí)驗(yàn).)98 d6 fc e5 fb 7f 00 00
對(duì)象的MethodTable
,根據(jù)類型而不同,對(duì)象的引用指向的位置。02 00 00 00
字符串長(zhǎng)度,這里是2。31 00 32 00
字符串?dāng)?shù)組* char
,注意都是小端模式。
拿以上s1 s2 o3 o4
分別實(shí)驗(yàn)可以發(fā)現(xiàn)他們的內(nèi)存一模一樣,其中s1 s2
直接就是同一塊內(nèi)存地址,但剩下的內(nèi)存地址都不一樣。
比較與解答
e1 = true;
通過(guò)內(nèi)存看合情合理,畢竟都同一塊內(nèi)存了。e2 = false;
這里如果用的VS的版本比較高的話,也能看出來(lái)。因?yàn)檫@里VS會(huì)提示:可能非有意的引用比較。
既然是引用比較,內(nèi)存地址都不一樣,肯定是false了。但是如果vs版本不高的話則迷惑性就較大了,其實(shí)這里做的是
ReferenceEquals
的比較。e3 = true;
這里問(wèn)題出在.Net代碼里。字符串類型Equals
方法被重載了。
// Determines whether two strings match. public override bool Equals([NotNullWhen(true)] object? obj) { if (object.ReferenceEquals(this, obj)) return true; if (!(obj is string str)) return false; if (this.Length != str.Length) return false; return EqualsHelper(this, str); }
EqualsHelper
方法最終則調(diào)用如下。(在.Net 6下)
// Optimized byte-based SequenceEquals. The "length" parameter for this one is declared a nuint rather than int as we also use it for types other than byte // where the length can exceed 2Gb once scaled by sizeof(T). public static unsafe bool SequenceEqual(ref byte first, ref byte second, nuint length)
由于實(shí)現(xiàn)過(guò)于復(fù)雜(.Net framework 4.5.2下則較簡(jiǎn)單,直接按長(zhǎng)度比較char,有興趣的自行查閱),這里就不貼具體實(shí)現(xiàn)了。我們很容易看出這里比較的目的是比較兩段內(nèi)存是否相等,顯然為true
。
e4 = false;
這里是為了比較不同序列化方式的影響,和e2
類似,結(jié)果顯然是false
。
結(jié)論
雖然.Net中字符串是享元模式創(chuàng)建的,但并不能保證同一字符串在內(nèi)存里只有一份。比如序列化情況等例外情況。如果讀者知道其他情況也可以告訴我,提前說(shuō)聲感謝
到此這篇關(guān)于詳解.Net中字符串不變性與相等判斷的特殊場(chǎng)景的文章就介紹到這了,更多相關(guān).Net中字符串不變性與相等判斷內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C#、.Net中把字符串(String)格式轉(zhuǎn)換為DateTime類型的三種方法
- ASP.NET web.config中數(shù)據(jù)庫(kù)連接字符串connectionStrings節(jié)的配置方法
- c# 連接字符串?dāng)?shù)據(jù)庫(kù)服務(wù)器端口號(hào) .net狀態(tài)服務(wù)器端口號(hào)
- asp.net Split分割字符串的方法
- ASP.NET 字符串截取
- C#.net格式化時(shí)間字符串達(dá)到不同的顯示效果
- asp.net 字符串加密解密技術(shù)
- asp.net(c#) 使用Rex正則來(lái)生成字符串?dāng)?shù)組的代碼
相關(guān)文章
Asp.Net+XML操作基類(修改,刪除,新增,創(chuàng)建)
更新內(nèi)容: 1,根據(jù)父節(jié)點(diǎn)屬性讀取字節(jié)點(diǎn)值 2,根據(jù)節(jié)點(diǎn)屬性讀取子節(jié)點(diǎn)值(較省資源模式)2008-07-07.Net Core實(shí)現(xiàn)圖片文件上傳下載功能
這篇文章主要為大家詳細(xì)介紹了.Net Core實(shí)現(xiàn)圖片文件上傳下載功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06asp.net core 騰訊驗(yàn)證碼的接入示例代碼
這篇文章主要介紹了asp.net core 騰訊驗(yàn)證碼的接入示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10asp.net中讓Repeater和GridView支持DataPager分頁(yè)
.NET 3.5中的DataPager碉堡了,可惜只支持ListView。傳統(tǒng)的GridView和Repeater都無(wú)法直接使用DataPager分頁(yè)。但我們?nèi)绻约痈脑欤涂梢宰孯epeater和GridView支持DataPager分頁(yè)2012-02-02ajaxpro.dll 控件實(shí)現(xiàn)異步刷新頁(yè)面
用ajaxpro.dll控件實(shí)現(xiàn)異步刷新頁(yè)面的代碼,需要的朋友可以參考下。2009-11-11ASP.NET Core+Docker+Jenkins實(shí)現(xiàn)持續(xù)集成的完整實(shí)例
這篇文章主要給大家介紹了關(guān)于ASP.NET Core+Docker+Jenkins實(shí)現(xiàn)持續(xù)集成的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05HttpRequest的QueryString屬性 的一點(diǎn)認(rèn)識(shí)
我們開(kāi)發(fā)asp.net程序獲取QueryString時(shí),經(jīng)常性的遇到一些url編碼問(wèn)題2012-11-11批量刪除記錄時(shí)如何實(shí)現(xiàn)全選方法總結(jié)
批量刪除記錄時(shí)如何實(shí)現(xiàn)全選方法總結(jié)...2007-04-04