深入理解C#中的String

来源:http://www.cnblogs.com/qingsp/archive/2017/05/26/6907553.html
-Advertisement-
Play Games

關於C 中的類型 在C 中類型分為值類型和引用類型,引用類型和值類型都繼承自System.Object類,幾乎所有的引用類型都直接從System.Object繼承,而值類型具體一點則繼承System.Object的子類,即繼承System.ValueType。而String類型卻有點特別,雖然它屬於 ...


關於C#中的類型

在C#中類型分為值類型和引用類型,引用類型和值類型都繼承自System.Object類,幾乎所有的引用類型都直接從System.Object繼承,而值類型具體一點則繼承System.Object的子類,即繼承System.ValueType。而String類型卻有點特別,雖然它屬於引用類型,但是他的一些特性卻有點類似值類型。

關於C# String

1、不變性

我們先來看看一個例子:

static void Main(string[] args)
{
    string str1 = "string";
    string str2 = str1;
    Console.WriteLine(object.ReferenceEquals(str1, str2));
    str2 += "change";
    Console.WriteLine(object.ReferenceEquals(str1, str2));
    Console.ReadKey();
}

輸出結果是True、False。為什麼呢?我們來看看IL。

.entrypoint
  // 代碼大小       48 (0x30)
  .maxstack  2
  .locals init ([0] string str1,
           [1] string str2)
  IL_0000:  nop
  IL_0001:  ldstr      "string"
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  stloc.1
  IL_0009:  ldloc.0
  IL_000a:  ldloc.1
  IL_000b:  ceq
  IL_000d:  call       void [mscorlib]System.Console::WriteLine(bool)
  IL_0012:  nop
  IL_0013:  ldloc.1
  IL_0014:  ldstr      "change"
  IL_0019:  call       string [mscorlib]System.String::Concat(string,string) 
  IL_001e:  stloc.1
  IL_001f:  ldloc.0
  IL_0020:  ldloc.1
  IL_0021:  ceq
  IL_0023:  call       void [mscorlib]System.Console::WriteLine(bool)
  IL_0028:  nop
  IL_0029:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
  IL_002e:  pop
  IL_002f:  ret

+=在內部調用了Concat函數,將str2和"change"連接起來直接生成了一個新的字元串,和原來的字元串是不同的對象。Trim、Remove函數都是會直接生成一個新的對象,字元串一經定義,就不能改變。

其實字元串具有原子性(也就是不變性),任何改變字元串的值的行為都不會成功,只會創建一個新的字元串對象。在實際編程中,我們會大量的使用字元串,這樣就會導致不停地創建新的字元串對象和分配記憶體,可能導致垃圾回收器GC不停地進行垃圾回收,大大降低性能,並且伴隨著記憶體溢出的危險。所以.Net對字元串進行了的特殊的處理,這就是字元串駐留池。

在字元串駐留池,保存著字元串字面值和指向的引用。每次有新的字元串創建,都會在駐留池中查找是否存在字面值相同的字元串,如果存在就將其指向已經存在的字元串的引用,不存在就直接新建一個字元串,然後指向一個新的地址。

2、作為函數參數的處理

在函數的參數傳遞中,值類型直接拷貝變數保存的值,傳遞的是一個值得副本,而引用類型傳遞的是地址的一個副本,所以在函數中改變引用參數中屬性的值會直接改變函數外真實類型對象的值。

static void Main(string[] args)
{
    People people = new People() { Name = "Jack" };
    Console.WriteLine(people.Name);
    Change(people);
    Console.WriteLine(people.Name);
    Console.ReadKey();
}

static void Change(People p)
{
    p.Name = "Eason";
}

class People
{
    public string Name { get; set; }
}

程式先輸出Jack,後輸出Eason,可以說明引用類型傳遞的是引用地址,函數改變的參數對象和外部傳遞進來的對象是一個對象。

那麼我們來看看String作為參數的情況:

static void Main(string[] args)
{
    string str = "string";
    Console.WriteLine(str);
    Change(str);
    Console.WriteLine(str);
    Console.ReadKey();
}

static void Change(string str)
{
    str = "change";
    Console.WriteLine(str);
}

結果輸出string、change、string。調用Change函數後str的值還是"string",由於字元串類型的不變性,在Change函數中對str進行賦值會重新創建一個新的字元串對象,然後為這個新的對象附上引用。所以雖然字元串類型是引用類型,但是在參數傳遞時它其實相當於值類型。

3、相等比較處理

先看一個例子:

string str1 = "string";
string str2 = "string";
string str3 = "stringstring";
string str4 = "string" + "string";
string str5 = str1 + "string";
Console.WriteLine(ReferenceEquals(str1, str2));
Console.WriteLine(str1 == str2);
Console.WriteLine(ReferenceEquals(str3, str4));
Console.WriteLine(str3 == str4);
Console.WriteLine(ReferenceEquals(str3, str5));
Console.WriteLine(str3 == str5);
Console.ReadKey();

不出意外結果都應該為True,True,True,True,True,True,但是結果卻是True,True,True,True,False,True,str3和str5不是一個對象,他們不是指向同一個地址,為什麼呢?經過查看IL代碼發現,str5在IL代碼中調用了Concat函數將str1和"string"進行了拼接,那這個Concat函數到底做了什麼。

public static string Concat(string str0, string str1)
{
    if (IsNullOrEmpty(str0))
    {
        if (IsNullOrEmpty(str1))
        {
            return Empty;
        }
        return str1;
    }
    if (IsNullOrEmpty(str1))
    {
        return str0;
    }
    int length = str0.Length;
    string dest = FastAllocateString(length + str1.Length);
    FillStringChecked(dest, 0, str0);
    FillStringChecked(dest, length, str1);
    return dest;
}

FastAllocateString函數負責分配長度為str0.Length+str1.Length的空字元串dest,FillStringChecked分別將str0和str1複製到dest中,最後生成由str0和str1連接成的字元串,這樣不會再去字元串駐留池中查找是否存在和dest相同的字元串,而是直接生成一個新的對象。所以字元串變數和字元串常量進行拼接後會直接生成一個新的對象,繞過駐留池檢查。

而字元串常量拼接不會產生新的字元串,除非駐留池中沒有與之拼接後字面值相等的字元串。我們來看看IL代碼:

  IL_0001:  ldstr      "string"
  IL_0006:  stloc.0
  IL_0007:  ldstr      "string"
  IL_000c:  stloc.1
  IL_000d:  ldstr      "stringstring"
  IL_0012:  stloc.2
  IL_0013:  ldstr      "stringstring"
  IL_0018:  stloc.3
  IL_0019:  ldloc.0
  IL_001a:  ldstr      "string"
  IL_001f:  call       string [mscorlib]System.String::Concat(string,string)
  IL_0024:  stloc.s    str5
  IL_0026:  ldloc.0
  IL_0027:  ldloc.1

str3和str4的字面值是相等的,都是"stringstring",str3先於str4被初始化,當str4被初始化的時候,由於其字面值和str3相等,所以CLR會將str3指向的地址賦給str4,所以str3和str4引用是相等的。

至於"=="操作符的得到的結果都是True是因為"=="操作符會調用String.Equal方法,IL代碼如下:

  IL_0032:  call       bool [mscorlib]System.String::op_Equality(string,string)

op_Equality最終會調用String.Equal函數,Equal函數的比較步驟是先比較兩個對象的引用是否相等,不相等的話再對值進行比較,比較值時是按位比較的。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 集群信息 角色 IP地址 ServerID 類型 Master 192.168.244.10 1 寫入 Candicate master 192.168.244.20 2 讀 Slave 192.168.244.30 3 讀 Monitor host 192.168.244.40 監控集群組 MHA ...
  • 一、如何查看Redis性能 info命令輸出的數據可以分為10個分類,分別是: server,clients,memory,persistence,stats,replication,cpu,commandstats,cluster,keyspace 為了快速定位並解決性能問題,這裡選擇5個關鍵性的 ...
  • 描述 最近有業務需求需瞭解客戶的伺服器SQLserver 的IO情況,而不僅僅是通過系統計數器 瞭解硬碟的IO情況或者使用CrystalDiskMark或者Trace重播進行壓力測試等 。這時SQL SERVER 2016自帶的SQLIOSIM工具就能很好解決這個需求,工具的預設路徑在D:\Prog ...
  • 一個完整的資料庫部署架構通常由客戶端和伺服器端兩部分組成。客戶端封裝資料庫請求將其發送給伺服器端,伺服器端執行完畢將其及結果返回給伺服器端。 以mysql為例 介紹java應用程式對資料庫的訪問 JDBC意義:應用程式通過調用統一介面實現對任意資料庫的訪問,為我們屏蔽了客戶端與伺服器端交互協議的實現 ...
  • 今天看到了一個新聞,跟大家分享一下,有興趣的可以去嘗試一下。 SQL Server 2017 CTP3於5月23日發佈了,詳細版本號是6.7.55.0。 大家可以去安裝試試。在下載頁面,目前是SQL Server vNext。預計下一個CTP版本會把所有2017的字樣都更新成vNext,計劃是六月份 ...
  • 之前手賤吧,拿濕抹布擦了擦筆記本電腦的自帶鍵盤,然後部分按鍵失靈了。 本想著反正也都是在寢室用的,趁機找藉口買了個機械鍵盤,啪啪啪... 剛開始好好的,後來發現一按shift就會粘貼,百度了下都說是粘滯鍵,可明明設置里是沒開的 換了個外接鍵盤發現也是如此,因此排除了外接鍵盤的問題。 就想著辦法想禁用 ...
  • Parallel類(http://www.cnblogs.com/afei-24/p/6904179.html)的並行任務需要結束後才能運行後面的代碼,如果想不等結束後在開始動作,可以使用Task類更好地控制並行動作。 任務表示應完成的某個工作單元。這個工作單元可以在單獨的線程中運行,也可以以同步方 ...
  • 轉自:http://www.cnblogs.com/gameman/p/3773240.html 為了學習ORM,選擇了EntityFramework,經歷了三天兩夜的煎熬,N多次錯誤,在群里高手的幫助下,終於成功,現在將我的心路歷程記錄下來,一是讓自己有個記錄,另外就是讓其它人少走些彎路。 我的開 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...