###### tags: `學習記錄` `C#`
# String 的新建與串接
字串中的每個字元都是由 Unicode 純量值所定義,也稱為 Unicode 程式碼點或 Unicode 字元的序數 (數值) 值。 每個程式碼點都是使用 UTF-16 編碼進行編碼,而編碼的每個專案的數值會以物件來表示 Char 。
字串是用來表示文字的連續字元集合。
String記憶體中物件的大小上限為 2 GB 或大約1000000000個字元。
字串的操作通常考量以下兩點:
1. 封裝次數
2. 記憶體消耗
## 字串物件的不變性
字串物件為「不可變」:它們在建立之後將無法變更。
所有看似會修改字串的 String 方法和 C# 運算子,實際上會以新的字串物件傳回結果。
```csharp=
string s1 = "A string is more ";
string s2 = "than the sum of its chars.";
// Concatenate s1 and s2. This actually creates a new
// string object and stores it in s1, releasing the
// reference to the original object.
s1 += s2;
System.Console.WriteLine(s1);
// Output: A string is more than the sum of its chars.
```
:::info
當 s1 和 s2 的內容串連以組成單一字串時,兩個原始字串將不會被修改。 += 運算子會建立新的字串,其中包含結合的內容。 新的物件會指派給變數 s1,而先前指派給 s1 的原始物件將會被釋放以進行記憶體回收,因為已經沒有其他具有其參考的變數。
:::
```csharp=
string s1 = "Hello ";
string s2 = s1;
s1 += "World";
System.Console.WriteLine(s2);
//Output: Hello
```
:::info
因為對字串的「修改」實際上是建立新的字串,建立對字串的參考時,必須特別謹慎。 如果建立對字串的參考,然後「修改」原始字串,該參考將會繼續指向原始物件,而非修改字串時所建立的新物件。
:::
String物件稱為「不可變」 (唯讀) ,因為它在建立之後無法修改其值。 出現來修改物件的方法,實際上會傳回 String String 包含修改的新物件。因為字串是不可變的,所以執行重複新增或刪除看似單一字串的字串操作常式,可能會對效能造成顯著的影響。
## 使用 StringBuilder 進行快速字串建立
可以使用 StringBuilder 類別,而不是對 String 字串值進行多次變更之作業的類別。與類別的實例不同的 String 是,物件是可變動的 StringBuilder ; 當串連、附加或刪除字串中的子字串時,會在單一字串上執行作業。當完成修改物件的值時 StringBuilder ,可以呼叫其方法,將 StringBuilder.ToString 它轉換成字串。
StringBuilder 類別會建立一個字串緩衝區,能在程式執行許多字串操作的情況下提供較佳的效能。 StringBuilder 字串也可重新指派內建字串資料類型所不支援的個別字元。
```csharp=
System.Text.StringBuilder sb = new System.Text.StringBuilder("Rat: the ideal pet");
sb[0] = 'C';
System.Console.WriteLine(sb.ToString());
System.Console.ReadLine();
//Outputs Cat: the ideal pet
```
## 合併字串
除了StringBuilder的串接字串,下列 String 方法可用於字串串連:
* [Concat](https://docs.microsoft.com/zh-tw/dotnet/api/system.string.concat?view=net-6.0) 將一個或多個子字串合併成單一字串。
* [Join](https://docs.microsoft.com/zh-tw/dotnet/api/system.string.join?view=net-6.0) 將一個或多個子字串串連成單一元素,並在每個子字串之間加入分隔符號。
### 格式值
String.Format方法會使用複合格式功能,以某個物件或值的字串表示,取代字串中的一個或多個預留位置。 Format方法通常用來執行下列作業:
* 在字串中內嵌數值的字串表示。
* 在字串中內嵌日期和時間值的字串表示。
* 在字串中內嵌列舉值的字串表示。
* 若要在字串中內嵌支援介面之某個物件的字串表示 IFormattable 。
* 以靠右對齊或靠左對齊較大字串內欄位中的子字串。
## 動態測試結果和程式碼

```csharp=
Stopwatch stopWatch = new Stopwatch();
string titleA = "StringBuilder";
stopWatch.Restart();
string A = null;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100000; i++)
{
sb.Append(i);
}
A = sb.ToString();
stopWatch.Stop();
Console.WriteLine("{0} \t 執行時間: {1}", titleA, stopWatch.Elapsed);
string titleB = "string +";
stopWatch.Restart();
string B = null;
for (int i = 0; i < 100000; i++)
{
B += i;
}
stopWatch.Stop();
Console.WriteLine("{0} \t 執行時間: {1}", titleB, stopWatch.Elapsed);
string titleC = "string.concat";
stopWatch.Restart();
string C = null;
for (int i = 0; i < 100000; i++)
{
C = string.Concat(C, i);
}
stopWatch.Stop();
Console.WriteLine("{0} \t 執行時間: {1}", titleC, stopWatch.Elapsed);
string titleD = "string.join";
stopWatch.Restart();
string D = null;
for (int i = 0; i < 100000; i++)
{
D = string.Join("", D, i);
}
stopWatch.Stop();
Console.WriteLine("{0} \t 執行時間: {1}", titleD, stopWatch.Elapsed);
string titleE = "string.Format";
stopWatch.Restart();
string E = null;
for (int i = 0; i < 100000; i++)
{
E = string.Format("{0}{1}", E, i);
}
stopWatch.Stop();
Console.WriteLine("{0} \t 執行時間: {1}", titleE, stopWatch.Elapsed);
string titleF = "Interpolation";
stopWatch.Restart();
string F = null;
for (int i = 0; i < 100000; i++)
{
F = $"{F}{i}";
}
stopWatch.Stop();
Console.WriteLine("{0} \t 執行時間: {1}", titleF, stopWatch.Elapsed);
Console.WriteLine("\nStringBuilder 和其他方法的產出內容是否相同");
Console.WriteLine(A.Equals(B));
Console.WriteLine(A.Equals(C));
Console.WriteLine(A.Equals(D));
Console.WriteLine(A.Equals(E));
Console.WriteLine(A.Equals(F));
```