C#.Net築基-String字串超全總結 [深度好文]

安木夕發表於2024-06-06

image.png

字串是日常編碼中最常用的引用型別了,可能沒有之一,加上字串的不可變性、駐留性,很容易產生效能問題,因此必須全面瞭解一下。


01、字元與字元編碼

1.1、字元Char

字元 char 表示為 Unicode字元,在C#中用 UTF-16 編碼表示,佔用2個位元組(16位)大小,字面量用單引號''包裹。

char c = 'A';
Console.WriteLine(char.IsDigit('3'));
Console.WriteLine(char.IsNumber('1'));
Console.WriteLine(char.IsLetter('A'));
Console.WriteLine(char.IsLower('a'));
Console.WriteLine(char.IsUpper('A'));
Console.WriteLine(char.GetUnicodeCategory('A')); //獲取字元分類
  • char 是值型別(結構體),以16位整數形式儲存,char可隱式轉換為int
  • 字串可以看做是char序列(陣列),字串是引用型別。

image.png

string str = "Hello World";
Console.WriteLine(str[0]);  //H
Console.WriteLine(str[10]); //d
Console.WriteLine(str[0].GetType().Name); //Char

1.2、字符集Unicode與字元編碼

一般情況下字串長度string.Length 就是可見的文字字元數量,但這並不絕對相等。大多數字符都是一個char組成,然而有些字元無法用一個char表示,如表情、不常用字元等,他們會用兩個char(4個位元組)來表示。

"a".Length.Dump();    //1
"🔊".Length.Dump();  //2
"🚩".Length.Dump();  //2
"⏰".Length.Dump();  //1
"你好".Length.Dump(); //2
"臢".Length.Dump();   //1
$"{(int)'A':X4}".Dump(); //0041
//上面的dump() 是一個擴充套件方法,作用同Console.WritLine()

Unicode 是國際標準、通用字符集,涵蓋了世界上幾乎所有的文字、符號,可以滿足跨平臺、跨語言的文字資訊編碼。Unicode 有100W+個字元地址空間,地址範圍是 0x0000 - 0x10FFFF,每個字元都有自己的編碼,目前已分配了大約10W+個。通常使用“U+”後跟一個十六進位制數來表示,例如字母A的Unicode碼點是U+0041

Unicode 字符集中包含多個分類(平面):其中最常用的就是基本平面,大部分常用字元都在這裡面。

  • 🔸基本多文種平面(BMP,Basic Multilingual Plane):Unicode 的BMP區域幾乎包含了所有常用的字元,如幾十種主流語言,及30000+的漢字,BMP區域的字元都只需要1個char(2個位元組)表示。
  • 🔸輔助平面(SMP):包含其他不常使用的字元,如一些歷史文字、音樂符號、數學符號和表情符號等。該區域大多用兩個char(4個位元組)表示一個符號。

image.png

Unicode 是一種字符集,而實際在計算機上儲存時需要用一個確定的編碼方案,常見的就是UTF-8、UTF-16、UTF32。

  • UTF-16:2個位元組表示BMP中的字元,其他字元會需要4個位元組,C#、Java語言內部就是使用的UTF-16來表示的字串。
  • UTF-8:變長編碼,使用1到4個位元組來表示一個Unicode字元,在網際網路使用廣泛。特別是儲存 ASCII 為主的內容時,變長編碼可以顯著節約儲存空間。

📢ASCII 字符集只包含 128個 基礎字元,涵蓋鍵盤上的字母、數字、常用符號。Unicode 是包含 ASCII字符集的,最前面128 個字元就是。在UTF-8編碼中 ASCII字元只需要1個位元組。


02、String基礎

字串 string 是一個不可變(不可修改)的字元序列(陣列),為引用型別,字面量用雙引號""包裹。

string s1 = "sam";
string s2 = new string('1',5);//11111
Console.WriteLine(s2[0]); //像陣列一樣操作字串中的字元
string s3 = "";
string s4 = string.Empty; //效果同上
//相等比較
object s1= "Hello".Substring(0,2);
object s2 = "Hello".Substring(0,2);	
(s1==s2).Dump();        //False
(s1.Equals(s2)).Dump(); //True
  • 字串是引用型別,因此可以用null表示,不過一般空字元建議用string.Empty(或"")表示。
  • 字串可以當做 字元陣列一樣操作,只是不能修改。
  • 字串的相等為值比較,只要字元序列相同即可。例外情況請是如果用object==比較,只會比較引用地址。

image.png

🚩 字串在儲存、轉換為位元組碼時需指定編碼,一般預設為 UTF-8,這是廣泛使用的編碼型別,更節省空間。

2.1、字串常用API

屬性 特點/說明
Length 字串中字元數量
索引器[int index] 索引器,用索引獲取字元,不可修改
🔸方法 特點/說明
StartsWithEndsWith(String) 判斷開頭、結尾是否匹配,"Hello".StartsWith("He")
Equals(String) 比較字串是否相同
IndexOf() 查詢指定字元(串)的索引位置,從後往前查詢 LastIndexOf
Insert(Int32, String) 指定位置插入字串,‼️返回新字串!
PadLeft(Int32) 指定字元寬度(數量)對齊,左側填充,‼️返回新字串!右側填充 PadRight(Int32)
Remove(Int32, Int32) 刪除指定位置、長度的字元,‼️返回新字串!
Replace(String, String) 替換指定內容的字元(串),‼️返回新字串!
Substring(Int32, Int32) 擷取指定位置、長度的字串,‼️返回新字串!
ToLower()ToUpper() 返回小寫、大寫形式的字串,‼️返回新字串!
Trim() 裁剪掉前後空格,‼️返回新字串!有多個配套方法 TrimEndTrimStart
Split(char) 按分隔符分割字串為多個子串,比較常用,不過效能不好,建議用Span代替。
🔸靜態方法 特點/說明
Empty 獲取一個空字串(同""
Compare(String, String) 比較兩個字串,有很多過載,返回一個整數,0表示相同。
Concat (params string?[]) 連線多個字串,返回一個新的字串,有很多過載,是比較基礎的字串連線函式。
Equals(str, StringComparison) 比較字串是否相同,可指定比較規則 StringComparison
Format(String, Object[]) 字串格式化,遠古時期常用的字串格式化方式,現在多實用$插值
string Intern(String) 獲取“內部”字串,先檢查字串池中是否存在,有則返回其引用,沒有則新增並返回
string? IsInterned(String) 判斷是否在字串池中,存在則返回其引用,沒有則返回null
IsNullOrEmpty(String) 判斷指定的字串是否 null 、空字元""/String.Empty,返回bool
IsNullOrWhiteSpace(String) 判斷指定的字串是否 null 、空字元""/String.Empty、空格字元,返回bool
Join(Char, String[]) 用分隔符連線一個陣列為一個字串

2.2、字串的不變性、駐留性

字串是一種有一點點特別的引用型別,因為其不變性,所以在引數傳遞時有點像值型別。

  • 🔸不變性:字串一經建立,值不可變。對字串的各種修改操作都會建立新的字串物件,這一點要非常重視,應儘量避免,較少不必要的記憶體開銷。
  • 🔸駐留性:執行時將字串值儲存在“駐留池(字串池)”中,相同值的字串都複用同一地址。

不變性、駐留性 是.Net對string 的效能最佳化,提升字串的處理效能。如下示例中,s1、s2字串是同一個引用。

string s1 = "hello";
string s2 = "hello";
Console.WriteLine(s1 == s2);                      //True
Console.WriteLine(s1.Equals(s2));                 //True
Console.WriteLine(Object.ReferenceEquals(s1,s2)); //True

當然不是所有字串都會駐留,那樣駐留池不就撐爆了嗎!一般只有兩種情況下字串會被駐留:

  • 字面量的字串,這在編譯階段就能確定的“字串常量值”。相同值的字串只會分配一次,後面的就會複用同一引用。
  • 透過 string.Intern(string) 方法主動新增駐留池。

image.png

string st1 = "123" + "abc";
string st2 = "123abc";
string st3 = st2.Substring(0,3);

看看上面程式碼生成的IL程式碼:

image.png

  • 常量的字串"123" + "abc"連線被編譯器最佳化了。
  • 常量字串使用指令“ldstr”載入的到棧,該指令會先檢視駐留池中是否已存在,如果已存在則直接返回已有字串物件的地址,否則就加入。

image.png

駐留的字串(字串池)在託管堆上儲存,大家共享,內部其實是一個雜湊表,儲存被駐留的字串和其記憶體地址。駐留池生命週期同程序,並不受GC管理,因此無法被回收。因此需要注意:

  • lock鎖不能用string,避免使用同一個鎖(字串引用)。
  • 避免建立字面量的大字串,會常住記憶體無法釋放,當然也不要濫用string.Intern(string) 方法。

2.3、字串的查詢、比較

string 的 比較字串 是預設包含文化和區分大小寫的順序比較,C#內建的一個字串比較規則(列舉)StringComparison,可設定比較規則。在很多內建方法中使用,包括 String.Equals、String.Compare、String.IndexOf 和 String.StartsWith等。

📢 微軟官方建議在使用上述字串比較方法中明確指定 StringComparison 引數值,而不是預設的比較規則。

public enum StringComparison
{
	CurrentCulture,
	CurrentCultureIgnoreCase,
	InvariantCulture,
	InvariantCultureIgnoreCase,
	Ordinal,
	OrdinalIgnoreCase
}
void Main()
{
	string.Equals("ABC","abc",StringComparison.Ordinal);           //Fasle
	string.Equals("ABC","abc",StringComparison.OrdinalIgnoreCase); //True
	string.Compare("ABC","abc",StringComparison.Ordinal);          //-32
	string.Compare("ABC","abc",StringComparison.OrdinalIgnoreCase);//0
}
列舉值 說明
CurrentCulture 本地語言區域規則,適用於給使用者顯示的內容
CurrentCultureIgnoreCase 同上+忽略大小寫
InvariantCulture 固定語言區域,適用於儲存的資料
InvariantCultureIgnoreCase 同上+忽略大小寫
Ordinal 二進位制值順序比較字串,比較快⚡
OrdinalIgnoreCase 同上+忽略大小寫

如果單純從效能角度考慮,考慮語言文化的字串比較其實比較慢,來測試對比一下。測試程式碼:

string s1 = "hellohellohellohello";
string s2 = "helloHelloHelloHello";

public bool Equals() => s1.Equals(s2);//False

public bool Equals_CurrentCulture() => s1.Equals(s2,StringComparison.CurrentCulture);//False
public bool Equals_CurrentCultureIgnoreCase() => s1.Equals(s2,StringComparison.CurrentCultureIgnoreCase);//True
public bool Equals_InvariantCulture() => s1.Equals(s2,StringComparison.InvariantCulture);//False
public bool Equals_InvariantCultureIgnoreCase() => s1.Equals(s2,StringComparison.InvariantCultureIgnoreCase);//True
public bool Equals_Ordinal() => s1.Equals(s2,StringComparison.Ordinal);//False
public bool Equals_OrdinalIgnoreCase() => s1.Equals(s2,StringComparison.OrdinalIgnoreCase);//True

public bool Equals_Span() => s1.AsSpan() == s2.AsSpan();//False
  • 上面7個方法 分別測試了Equals的預設版本、及帶參 StringComparison 的不同比較規則的效能。
  • 最後加了一個使用Span 的相等比較,更多關於Span的資料檢視《高效能的Span、Memory》。

image.png

🚩測結結論

  • Span最快,其次無參Equals()版本、Ordinal,他們都是隻比較二進位制值,不考慮文化資訊。
  • 個人理解,如果不考慮一些比較特別的語言(如瑞典語、土耳其語、 亞塞拜然語等),只是針對英文、中文的字串,一般不用考慮文化語義。
  • Equals()預設是不考慮文化語義的字元值比較,但有些比較方法就不一定能了,比如StartsWithCompare 預設的是帶文化語義的CurrentCulture規則,因此推薦主動配置 StringComparison 引數。

2.4、字串轉義\

跳脫字元:反斜槓“\”

轉義序列 字元名稱 Unicode 編碼
\' 單引號 0x0027
\" 雙引號 0x0022
\0 null 0x0000
\b Backspace 0x0008
\f 換頁 0x000C
\n 換行 0x000A
\r 回車 0x000D
\t 水平製表符 0x0009

image.png


03、🚩字串連線的8種方式

字串連線(組裝)的使用是非常頻繁的,.Net中提供了多種姿勢來實現,各有特點。

連線方法 示例/說明
直接相加 "hello"+str,其實編譯後為 string.Concat ("hello", str)
連線函式:String.Concat() 字串相加一般就是被編譯為呼叫String.Concat()方法,有很多過載,支援任意多個引數
集合連線函式:String.Join() 將(集合)引數連線為一個字串,string.Join('-',1,2,3); //1-2-3
格式化:String.Format() 傳統的字串格式化手藝,string.Format("name:{0},age:{1}",str,18)
$ 字串插值 用花括號{var}引用變數、表示式,強大、方便,$"Hello {name} !"
@逐字文字字面量 支援轉義符號、換行符,常用於檔案路徑、多行字元:@$"C:\\Users\\{name}\\Downloads"
"""原始字串字面量 C# 11,三個雙冒號包圍,支援多行文字的原始字面量。
StringBuilder 當處理大量字串連線操作時,推薦使用StringBuilder,效果更優。

字面量字串的相加會被編譯器最佳化,直接合併為一個字串。

var str1 = "Hello " + "world" + " !";
var str2 = DateTime.Now.Year + "年" + DateTime.Now.Month + "月";

//編譯後的程式碼:
string str1 = "Hello world !";
string str2 = string.Concat (DateTime.Now.Year.ToString (), "年", DateTime.Now.Month.ToString (), "月");

3.1、字串格式化 String.Format

String.Format 方法是早期比較常用的字串組織方式,後來$字串插值 問世後就逐步被打入冷宮了。

string.Format("{0}+{1} = {2}",1,2,3);  //1+2 = 3
string.Format("Hello {0},{0}","sam");  //Hello sam,sam
String.Format("It is now {0:yyyy-MM-dd} at {0:hh:mm:ss}", DateTime.Now); //It is now 2024-01-17 at 10:56:33
String.Format("買了{0}個桔子,共花了{1:C2}。", 4,25.445); //買了4個桔子,共花了¥25.45。

基本語法規則就是用 {index}來佔位,在後面的引數中給出值。

  • 索引位置從0開始,必須連續遞增,可以重複。
  • 索引的位置對應後面引數的順序位置,必須對應,引數不能少(丟擲異常),可以多。
  • 字串格式規則參考後文《字串格式總結》。

3.2、$字串插值

字串插值的格式:$"{<interpolationExpression>}",大括號中可以是一個變數,一個(簡單)表示式語句,還支援設定格式。功能強大、使用方便,老人孩子都愛用!

  • {}字元轉義,用兩個{{}}即可,如果只有一邊,則用單引號'{{',即輸出為{
  • 使用三元運算子?表示式,用括號包起來即可,因為“:”在插值字串中有特殊含義,即格式化。
  • 字串格式規則參考後文《字串格式總結》。
var name = "sam";
Console.WriteLine($"Hello {name}!");  //Hello sam!
Console.WriteLine($"日期:{DateTime.Now.AddDays(1):yyyy-MM-dd HH:mm:ss}");  //日期:2024-01-18 23:21:55!
Console.WriteLine($"ThreadID:{Environment.CurrentManagedThreadId:0000}");  //ThreadID:0001
Console.WriteLine($"Length:{name.Length}");  //Length:3
Console.WriteLine($"Length:{(name.Length>3?"OK":"Error")}");  //Length:Error

3.3、@字串支援任意字元

@標記的字串為字面量字串 ,不需要使用跳脫字元了,可搭配$字串插值使用。檔案路徑地址都會用到@,兩個冒號表示一個冒號,@"a""b" ==a"b

var path= @"D:\GApp\LINQPad 8\x64";
var file = $@"D:\GApp\LINQPad 8\x64\{DateTime.Now:D}";
var maxText = @"Hi All:
	第一行
		換行
	";

3.4、👍🏻StringBuilder

StringBuilder 字串修理工程師,顧名思義,就是專門用來組裝字串的,可以看做是一個可變長字符集合。適用於把很多字串組裝到一起的場景,避免了大量臨時字串物件的建立,可顯著提升效能。

var sb = new StringBuilder(100);
sb.Append("sam");
sb[0] = 'F';  //Fam
sb.AppendLine("age");
sb.Append("age").Append(Environment.NewLine); //效果同上
sb.Insert(2,"---");
sb.Replace("age","Age");

var result = sb.ToString(); //獲取結果
屬性 特點/說明
Capacity 獲取、設定字元容量(實際佔用記憶體),預設16,當內容增多容量不足時,會自動擴容。
MaxCapacity 獲取最大容量,20億字元
Length 實際字元內容的長度,可賦值,設定0則清空已有字元內容,但並不影響 Capacity
Chars[Int32] 索引器,可獲取、設定字元
🔸方法 特點/說明
StringBuilder(Int32) 建構函式,引數指定初始容量capacity
Append(value) 追加字元,很多過載版本,類似還有AppendFormat、AppendJoin
AppendLine 追加字元後,再追加一個換行符
Insert (int index, value) 指定位置插入字元內容
Replace(Char, Char) 查詢替換字元(字串)內容,會替換所有找到的字元內容
ToString() 將 StringBuilder 輸出為一個字串,一般是StringBuilder的命運終點。
  • 各種Append方法都返回自身,可用來鏈式程式設計。
  • StringBuilder 預設容量為16,內部有一個char陣列m_ChunkChars(緩衝區)來儲存字元內容,如下StringBuilder建構函式原始碼
public StringBuilder()
{
	m_MaxCapacity = int.MaxValue;
	m_ChunkChars = new char[16];
}
  • 當不斷追加字串,容量不足會自動擴容,擴容的過程其實就是建立更大的字元陣列(容量翻倍),把原來的值複製過來,這個過程會涉及陣列物件建立、記憶體複製。

📢 一般使用StringBuilder 建議儘量給一個合理的預設容量大小,儘量避免、減少頻繁的擴容。


04、🚩字串格式化大全

📢字串格式語法:{index/interpolationExpression [,alignment][:formatString]}

  • ,alignment可選,設定字串的對齊長度,如果位數不夠則空格補齊,正數部補左邊,負數補右邊。
  • :formatString指定格式規則。一次只能指定一個格式規則,可和,alignment共存。
//,alignment 示例
var name = "sam";
$"name:{name,6}.";    //字元長度6,前面補齊空格 //name:   sam.
$"name:{name,-6}.";   //字元長度6,後面補齊空格 //name:sam   .
"1123+1 = {(1223+1),6:#,#.##}";                //1123+1 =  1,224
string.Format("1123+1 = {0,6:#,#.##}",1223+1); //1123+1 =  1,224

4.1、數值格式

🚩標準數值格式

🔸數值格式 說明
E3/e3 科學計數法(指數),數字"3"為小數精度,$"{12345.2:E3}" //1.235E+004E+4表示10的4次方;如果是E-4則表示為小數(除以10的四次方) 1E-4 = 0.0001
F4 定點格式,小數精度為"4",位數不夠後面補0,支援所有數值型別,$"{123.22F:F4}" //123.2200
G4 定點格式F+指數E的結合版,最多"4"個有效數字,超過就用科學計數法。"{123:G2}" //1.2E+02$"{123:G4}" //123
C3 貨幣格式(支援千分位),數字“3”為小數位數,$"{123.346:C2}" //¥123.35
P2 百分比格式,數字乘以100後轉換為百分數,數字“2”為小數位數,$"{0.2:P2}" //20.00%
N6 數字格式化(支援千分位),小數位數為6,不夠後面補0,$"{123:N6}" //123.000000
D6 整數定長格式,不夠前面補0,只支援整數,$"{123:D6}" //000123
B 輸出為二進位制格式,僅支援整數+.Net8,精度為字串位數,不夠補0,$"{123:B}" //1111011
X/x 輸出為十六進位制格式,僅支援整數+,精度為字串位數,不夠補0,$"{12:X4}" //000C

🚩自定義的數值格式

🔸數值格式符號 說明
# 數字佔位符,不強制佔位,$"{123:#,###.##}" //123
0 數字(0)佔位符,強制佔位,不夠補0。$"{123:0000.00}" //0123.00
. 小數點,
, 千分位,
, 倍數符號,也是逗號,在末尾、小數點前為倍數符號,除以1000,可多個。$"{12000:#,}" //12
% 百分數,乘一百+%,$"{0.2:00.00%}" //20.00%
E/e 指數(科學計數),$"{10.1234:0.00e0}" //1.01e1$"{0.01234:0.00e0}" //1.23e-2
\\ 跳脫字元,

📢熱知識:小數格式化截斷時都會四捨五入,(int)double 強轉換是直接截斷整數部分,相當於向下取整。
🔊冷知識:土耳其文化中的小數點為“逗號”,而非“點”。

4.2、日期時間格式

🔸日期格式-自定義 說明(DateTimeDateTimeOffset
yyyy 年份,yyyy //2024,yy //24
MM 2位數的月份,1個M就不會補0 了,3/4個M為月份名稱。M //4,MM //04,MMM //4月,MMMM //四月
dd 2位數的日,3/4個d為星期。d //8,dd //08,ddd //週一,dddd //星期一
HH 2位數的小時(24小時制)
hh 2位數的小時(12小時制)
mm 2位數的分鐘
ss 2位數的秒
f 為1/10秒單位,ff為1/100秒單位,以此類推,fff就表示毫秒
tt AM/PM 指示符
組合使用 以上可組合使用,可穿插任意字元,$"{DateTime.Now:yyyy年MM月dd日 HH:mm:ss}"
🔸日期格式-簡寫 說明
D、d D長日期,d短日期,$"{DateTime.Now:D}" //2024年1月18日
F、f 完整日期/時間模式,F長時間,f短時間,$"{DateTime.Now:F}" //2024年1月18日 22:45:34
T、t T長時間,t短時間,$"{DateTime.Now:T}" //22:45:42
M/m 月日模式,$"{DateTime.Now:M}" //1月18日
Y/y 年月模式,$"{DateTime.Now:Y}" //2024年1月

4.3、其他格式

🔸列舉格式 說明
G/g,F/f 列舉的字串名稱,其中F用於Flags,$"{UType.User:G}" //User
D/d 十進位制列舉值,$"{UType.User:D}" //2
X/x 十六進位制列舉值,$"{UType.User:X}" //00000002
🔸其他 說明
IFormattable 自定義的格式化介面,使用自定義的 IFormatProvider 來實現格式化輸出ToString()
NumberStyles 用於解析數字符串(Parse)時指定的解析格式
DateTimeStyles 同上,用於時間日期的解析

🚩格式MSDN參考資料

  • 所有整型和浮點型別。 (請參閱 標準數字格式字串自定義數值格式字串。)
  • DateTimeDateTimeOffset。 (請參閱 標準日期和時間格式字串自定義日期和時間格式字串。)
  • 所有列舉型別。 (請參閱 列舉格式字串.)
  • TimeSpan 值。 (請參閱 標準 TimeSpan 格式字串自定義 TimeSpan 格式字串。)
  • GUID。 (請參閱 Guid.ToString(String) 方法。)

06、高效能字串實踐

提高string處理效能的核心就是:儘量減少臨時字串物件的建立

  • 高頻常用字串(非字面量)可考慮主動駐留字串,string.Intern(name)
  • 字串的比較、查詢,優先用Span,或者儘量使用無文化語義的比較StringComparison.Ordinal
  • 大量字串連線使用StringBuilder,且儘量給定一個合適的容量大小,避免頻繁的擴容。
  • 少量字串連線用字串插值即可,建立StringBuilder也是有成本的。
  • 如果有大量StringBuilder 的使用,可以考慮用StringBuilderCache,或池化StringBuilder。

6.1、比較字串

  • 字串查詢、拆分字串、解析字串,推薦使用Span,參考《高效能的Span、Memory》。
  • 查詢、比較字串,儘量指定 StringComparisonOrdinalOrdinalIgnoreCase,採用無文化特徵的比較效能更快。
string str1="a",str2 = "b";
//這種方式會產生新的字串,不推薦
if(str1.ToLower() == str2.ToLower()){} 
//推薦寫法
if(string.Compare(str1, str2, true)==0){} 
if(string.Equals(str1,str2,StringComparison.Ordinal)){}

image.png

6.2、字串真的不能修改嗎?

字串其實也是可以修改的,當然是用非常規手段。

  • ref獲取指定字元的引用地址(指標地址)。
static void Main(string[] args)
{
	var str1 = "hello";
    var str2 = "hello";
	//修改第0位
    ref var c1 = ref MemoryMarshal.GetReference<char>(str1);
	c1 = 'H';
	//修改第一位
	ref var c2 = ref MemoryMarshal.GetReference<char>(str1.AsSpan(1));
	c2 = 'E';
	Console.WriteLine(str1);//輸出:HEllo
	Console.WriteLine(str2);//輸出:HEllo
}
  • 直接使用指標修改字元值。
void Main()
{
	var str1 = "hello";
	var str2 = "hello";
	unsafe
	{
		fixed (char* c = str2)
		{
			c[0] = 'H';
			c[1] = 'E';
		}
	}
	Console.WriteLine(str1); //HEllo
	Console.WriteLine(str2); //HEllo
}

參考資料

  • C# 文件
  • 《C#8.0 In a Nutshell》
  • .NET面試題解析(03)-string與字串操作
  • .NET 中的字元編碼

©️版權申明:版權所有@安木夕,本文內容僅供學習,歡迎指正、交流,轉載請註明出處!原文編輯地址-語雀

相關文章