C# 有許多內建參考類型。 這些類型包括與 .NET 函式庫中某型別同義的關鍵字或運算子。
C# 語言參考資料記錄了 C# 語言最新版本。 同時也包含即將推出語言版本公開預覽功能的初步文件。
文件中標示了語言最近三個版本或目前公開預覽版中首次引入的任何功能。
小提示
欲查詢某功能何時首次在 C# 中引入,請參閱 C# 語言版本歷史的條目。
物件型別
object 類型是 System.Object 在 .NET 中的別名。 在 C# 的統一型別系統中,所有類型 (預先定義和使用者定義的、參考型別和實值型別) 都會直接或間接繼承自 System.Object。 將任意型別的值(除 ref struct參考 ref struct)指派給型別 object為 的變數。 你可以將文字 null 值指派給任何 object 變數作為預設值。 當你將值型態變數轉換成 object時,該值會 被框住。 當你將 的 object 變數轉換成 值型態時,該值會 被開箱。 如需詳細資訊,請參閱 Boxing 和 Unboxing。
字串型別
string 類型代表零或多個 Unicode 字元序列。 在 .NET 中, string 是 的別名 System.String。
雖然 string 是參考類型,但等 式運算子 == 與 != 比較物件的值 string ,而非參考。 基於價值的等式使字串等式的測試更直觀。 例如:
string a = "hello";
string b = "h";
// Append to contents of 'b'.
b += "ello";
Console.WriteLine(a == b);
Console.WriteLine(object.ReferenceEquals(a, b));
前述範例顯示「True」後再顯示「False」,因為字串內容等價,但 a 和 b 並不指同一字串實例。
+ 運算子會串連字串:
string a = "good " + "morning";
上述程式碼會建立包含 "good morning" 的字串物件。
字串是 不可變 的——你在建立字串物件後無法更改它的內容。 例如,當你寫這段程式碼時,編譯器會建立一個新的字串物件來存放新的字元序列,並將該新物件指派給 b。 配置的 b 記憶體 (當它包含字串 “h” 時) 符合記憶體回收的資格。
string b = "h";
b += "ello";
你可以用這個 []運算元 來對字串的單一字元進行只讀存取。 有效的索引值從 0 開始,且必須小於字串的長度:
string str = "test";
char x = str[2]; // x = 's';
同樣地,你也可以使用運算 [] 子來遍歷字串中的每個字元:
string str = "test";
for (int i = 0; i < str.Length; i++)
{
Console.Write(str[i] + " ");
}
// Output: t e s t
字串常值
字串文字屬於型態 string ,有三種形式:原始文字、引用文字和逐字文字。
原始字串字面 包含任意文字,且不需跳脫序列。 原始字串常值可包含空白字元和新行、內嵌引號以及其他特殊字元。 原始字串常值會以至少三個雙引號括住 ("""):
"""
This is a multi-line
string literal with the second line indented.
"""
甚至可以包含三個 (或更多) 一連串的雙引號字元。 如果您的文字需要內嵌一連串的引號,可以視需要加上更多引號作為原始字串常值的開頭與結尾:
"""""
This raw string literal has four """", count them: """" four!
embedded quote characters in a sequence. That's why it starts and ends
with five double quotes.
You could extend this example with as many embedded quotes as needed for your text.
"""""
原始字串常值通常會在和內嵌文字不同的各自兩行,出現開頭和結尾的一連串引號。 多行原始字串常值,支援本身被引號括住的字串:
var message = """
"This is a very important message."
""";
Console.WriteLine(message);
// output: "This is a very important message."
當開頭和結尾引號位於不同行時,接在開頭引號之後但在結尾引號之前的新行,不會包含在最終內容中。 結尾的一連串引號,代表字串常值最左邊的資料行。 您可以縮排原始字串常值,以配合整體程式碼格式:
var message = """
"This is a very important message."
""";
Console.WriteLine(message);
// output: "This is a very important message."
// The leftmost whitespace is not part of the raw string literal
結尾一連串引號右側的資料行會保留。 此行為可形成資料格式 (JSON、YAML 或 XML 等) 的原始字串,如下範例所示:
var json= """
{
"prop": 0
}
""";
小提示
Visual Studio 和 C# 開發套件會在原始字串常值包含 JSON 資料或規則運算式時,提供一些驗證和語法醒目提示。
這些工具會解析文字。 如果工具確信文字代表 JSON 或規則運算式,編輯器會提供語法著色。
您可以在聲明上方新增註解,以指示格式,以改善該體驗:
-
// lang=json表示原始字串常值代表 JSON 資料。 -
// lang=regex表示原始字串常值代表正則表示式。
當原始字串常值用作引數時,其中參數使用 來 System.Diagnostics.CodeAnalysis.StringSyntaxAttribute 指示格式,這些工具會驗證某些格式類型的原始字串常值。 支援 JSON 和正則運算式。
對於某些格式,註解或屬性會啟用程式碼建議,根據格式提供字串常值的修正。
如果任何文字行延伸到結尾引號序列的左側,編譯器會回傳錯誤。 開頭和結尾的一連串引號,可位於同一行,前提是字串常值不會以引號字元作為開頭或結尾:
var shortText = """He said "hello!" this morning.""";
您可以將原始字串常值與字串插補相合併,在輸出字串中包含引號字元和大括弧。
以引號括住的字串常值是以雙引號 (") 括住:
"good morning" // a string literal
字串常值可以包含任何字元常值。 包含逸出序列。 下列範例使用逸出序列 \\ 表示反斜線、\u0066 表示字母 f,而 \n 表示新行字元。
string a = "\\\u0066\n F";
Console.WriteLine(a);
// Output:
// \f
// F
注意
逸出代碼 \udddd (其中 dddd 是四位數字) 代表 Unicode 字元 U+dddd。 也會辨識八位數 Unicode 逸出代碼︰\Udddddddd。
逐字字串常值的開頭為 @,也會用雙引號括住。 例如:
@"good morning" // a string literal
逐字字串的優點是跳脫序列 不會被 處理,因此容易撰寫。 例如,下列文字符合完整的 Windows 檔案名稱:
@"c:\Docs\Source\a.txt" // rather than "c:\\Docs\\Source\\a.txt"
若要在括了 @ 的字串中包含雙引號,請使用兩個引號︰
@"""Ahoy!"" cried the captain." // "Ahoy!" cried the captain.
UTF-8 字串常值
.NET 使用 UTF-16 編碼來儲存字串。 UTF-8 是 Web 通訊協定和其他重要程式庫的標準。 你可以在字串字面上加上 u8 後綴來指定 UTF-8 編碼。 編譯器會將 UTF-8 字面值儲存為 ReadOnlySpan<byte> 物件。 使用 UTF-8 字串常值,可建立比宣告對等 System.ReadOnlySpan<T> 更清楚的宣告,如下列程式碼所示:
ReadOnlySpan<byte> AuthWithTrailingSpace = new byte[] { 0x41, 0x55, 0x54, 0x48, 0x20 };
ReadOnlySpan<byte> AuthStringLiteral = "AUTH "u8;
要將 UTF-8 字串的字面值儲存為陣列,請將 ReadOnlySpan<T>.ToArray() 包含該字面的位元組複製到可變陣列:
byte[] AuthStringLiteral = "AUTH "u8.ToArray();
UTF-8 字串的字面量不是編譯時常數;它們是運行時間的常數。 因此,不能用作為選用參數的預設值。 你無法將 UTF-8 字串字面值與字串插值結合。 在同一個字串運算式上,無法同時使用 $ 權杖和 u8 尾碼。
委派型別
委派類型的宣告類似方法簽章。 它具有傳回值以及任意類型之任何數目的參數:
public delegate void MessageDelegate(string message);
public delegate int AnotherDelegate(MyType m, long num);
在 .NET 中,System.Action 和 System.Func 型別提供許多常見委派的泛型定義。 您多半不需要定義新的自訂委派型別。 反之,您可以建立所提供之泛型型別的具現化。
A delegate 是一種內建的參考型別,可以用來封裝命名或匿名的方法。 委派類似 C++ 中的函式指標,但委派是型別安全而且安全的。 如需委派的應用程式,請參閱委派和泛型委派。 委派是事件的基礎。 你可以透過將代理與命名或匿名方法關聯來實例化。
你必須用一個方法或 lambda 運算式實例化代理,且該表達式的回傳類型和輸入參數相容。 如需方法簽章中所允許變異程度的詳細資訊,請參閱 Variance in Delegates (委派中的變異數)。 用於匿名方法時,將代理與程式碼一同關聯。
執行階段涉及的委派類型因為變異轉換而不同時,委派組合或移除會因為執行階段的例外狀況而失敗。 下列範例示範失敗的情況:
Action<string> stringAction = str => {};
Action<object> objectAction = obj => {};
// Valid due to implicit reference conversion of
// objectAction to Action<string>, but might fail
// at run time.
Action<string> combination = stringAction + objectAction;
藉由建立新的委派物件,可以建立具有正確執行階段類型的委派。 下列範例示範如何將此因應措施套用至上述範例。
Action<string> stringAction = str => {};
Action<object> objectAction = obj => {};
// Creates a new delegate instance with a runtime type of Action<string>.
Action<string> wrappedObjectAction = new Action<string>(objectAction);
// The two Action<string> delegate instances can now be combined.
Action<string> combination = stringAction + wrappedObjectAction;
您可以宣告使用類似語法的函式指標。 函式指標會使用 calli 指令,而不是具現化委派類型並呼叫虛擬 Invoke 方法。
動態型別
型 dynamic 態表示變數及其成員的引用可繞過編譯時的型別檢查。 相反地,這些作業會在執行階段解決。
dynamic 類型會簡化 Office Automation API 這類 COM API 的存取、IronPython 程式庫這類動態 API 的存取,以及 HTML 文件物件模型 (DOM) 的存取。
在大多數情況下,dynamic 類型的行為與 object 類型類似。 特別是,任何非 Null 運算式都可轉換成 dynamic 型別。 型 dynamic 別與 不同 object 之處在於編譯器不會解析或型別檢查包含型別 dynamic為 的表達式的操作。 編譯器會將作業資訊封裝在一起,而且稍後在執行階段會使用這項資訊來評估作業。 在此程序期間,會將 dynamic 類型的變數編譯為 object 類型的變數。 因此,dynamic 類型只存在於編譯時期,而非執行階段。
以下範例對比型態 dynamic 為 的變數與型態 object為 的變數。 若要在編譯時期驗證每個變數的類型,請將滑鼠指標放在 dyn 陳述式中的 obj 或 WriteLine 上方。 將下列程式碼複製到可使用 IntelliSense 的編輯器。 IntelliSense 會顯示「動態」來表示 dyn,並顯示「物件」來表示 obj。
class Program
{
static void Main(string[] args)
{
dynamic dyn = 1;
object obj = 1;
// Rest the mouse pointer over dyn and obj to see their
// types at compile time.
System.Console.WriteLine(dyn.GetType());
System.Console.WriteLine(obj.GetType());
}
}
WriteLine 陳述式會顯示執行階段類型 dyn 和 obj。 此時,兩者都有相同的類型:整數。 此時會產生下列輸出:
System.Int32
System.Int32
若要查看 dyn 與 obj 在編譯時期的差異,請在上述範例的宣告與 WriteLine 陳述式之間新增下列兩行。
dyn = dyn + 3;
obj = obj + 3;
嘗試新增運算式 obj + 3 中的整數和物件時報告編譯器錯誤。 不過,不會回報 dyn + 3 的錯誤。 因為 dyn 的類型是 dyn,所以在編譯時期不會檢查包含 dynamic 的運算式。
下列範例會在數個宣告中使用 dynamic。
Main 方法也會對照編譯時期類型檢查與執行階段類型檢查。
using System;
namespace DynamicExamples
{
class Program
{
static void Main(string[] args)
{
ExampleClass ec = new ExampleClass();
Console.WriteLine(ec.ExampleMethod(10));
Console.WriteLine(ec.ExampleMethod("value"));
// The following line causes a compiler error because ExampleMethod
// takes only one argument.
//Console.WriteLine(ec.ExampleMethod(10, 4));
dynamic dynamic_ec = new ExampleClass();
Console.WriteLine(dynamic_ec.ExampleMethod(10));
// Because dynamic_ec is dynamic, the following call to ExampleMethod
// with two arguments does not produce an error at compile time.
// However, it does cause a run-time error.
//Console.WriteLine(dynamic_ec.ExampleMethod(10, 4));
}
}
class ExampleClass
{
static dynamic _field;
dynamic Prop { get; set; }
public dynamic ExampleMethod(dynamic d)
{
dynamic local = "Local variable";
int two = 2;
if (d is int)
{
return local;
}
else
{
return two;
}
}
}
}
// Results:
// Local variable
// 2
// Local variable
C# 語言規格
如需詳細資訊,請參閱 C# 語言規格的下列幾節: