宣告陳述式
宣告語句會宣告新的變數,並選擇性地初始化它。 所有變數都具有宣告類型。 您可以在 .NET 類型系統上的文章中深入瞭解類型。 一般而言,宣告包含類型和變數名稱。 它也可以包含初始化: =
運算子後面接著運算式。 型別可能會取代為 var
。 宣告或運算式可能包含 ref
修飾詞,以宣告新變數參考現有的儲存位置。
隱含型別區域變數
在方法範圍中宣告的變數可以有隱含的「類型」 var
。 隱含類型的區域變數是強型別,就像您自己宣告類型一樣,但編譯器會決定類型。 和 的下列兩個宣告 a
b
在功能上相等:
var a = 10; // Implicitly typed.
int b = 10; // Explicitly typed.
重要
當 var
與啟用 可為 Null 的參考型別 搭配使用時,它一律表示可為 Null 的參考型別,即使運算式類型不可為 Null 也一樣。 編譯器的 Null 狀態分析可防止取值可能 null
的值。 如果變數從未指派給可能為 null 的運算式,編譯器就不會發出任何警告。 如果您將變數指派給可能為 Null 的運算式,則必須在取值之前先測試它不是 Null,以避免任何警告。
關鍵字的 var
常見用法是搭配建構函式調用運算式。 使用 var
可讓您在變數宣告和物件具現化中重複類型名稱,如下列範例所示:
var xs = new List<int>();
從 C# 9.0 開始,您可以使用目標型別new
運算式作為替代方法:
List<int> xs = new();
List<int>? ys = new();
在模式比對中 var
,關鍵字會用於var
模式中。
下例示範兩個查詢運算式。 在第一個運算式中,允許使用 var
,但並非必要,因為查詢結果的類型可以明確陳述為 IEnumerable<string>
。 不過,在第二個運算式中, var
允許結果成為匿名型別的集合,而且除了編譯器本身之外,無法存取該類型的名稱。 使用 var
就不需要建立結果的新類別。 在範例 #2 中 foreach
,反復專案變數 item
也必須隱含類型。
// Example #1: var is optional when
// the select clause specifies a string
string[] words = { "apple", "strawberry", "grape", "peach", "banana" };
var wordQuery = from word in words
where word[0] == 'g'
select word;
// Because each element in the sequence is a string,
// not an anonymous type, var is optional here also.
foreach (string s in wordQuery)
{
Console.WriteLine(s);
}
// Example #2: var is required because
// the select clause specifies an anonymous type
var custQuery = from cust in customers
where cust.City == "Phoenix"
select new { cust.Name, cust.Phone };
// var must be used because each item
// in the sequence is an anonymous type
foreach (var item in custQuery)
{
Console.WriteLine("Name={0}, Phone={1}", item.Name, item.Phone);
}
ref 區域變數
您可以在變數類型之前新增 ref
關鍵字,以宣告 ref
區域變數。 假設方法 GetContactInformation
宣告為 ref 傳回:
public ref Person GetContactInformation(string fname, string lname)
傳值方式指派會讀取變數值,並將它指派給新的變數:
Person p = contacts.GetContactInformation("Brandie", "Best");
上述的指派將 p
宣告為區域變數。 其初始值的複製來源,是讀取 GetContactInformation
所傳回的值。 的任何未來指派 p
都不會變更 所 GetContactInformation
傳回之變數的值。 變數 p
不再是所傳回之變數的別名。
您可以宣告 ref 變數,以將別名複製到原始值。 在下列指派中,p
是 GetContactInformation
所傳回之變數的別名。
ref Person p = ref contacts.GetContactInformation("Brandie", "Best");
後續 p
的使用方式與使用 GetContactInformation
傳回的變數相同,因為 p
是該變數的別名。 變更 p
也會變更 GetContactInformation
傳回的變數。
您可透過相同方式以參考存取值。 在某些情況下,以參考存取值會避免潛在過度浪費資源的複製作業,進而增加效能。 例如,下列陳述式示範了如何定義用於參考值的區域變數值。
ref VeryLargeStruct reflocal = ref veryLargeStruct;
區域變數宣告的前面「和」第二個範例中值的前面都會使用 ref
關鍵字。 在兩個範例中,若未在變數宣告中包含這兩 ref
個關鍵字,則會導致編譯器錯誤 CS8172:「無法以值初始化位元組參考變數」。
ref VeryLargeStruct reflocal = ref veryLargeStruct; // initialization
refLocal = ref anotherVeryLargeStruct; // reassigned, refLocal refers to different storage.
在宣告參考區域變數時,仍必須初始化它們。
下列範例定義 NumberStore
類別,以儲存整數值的陣列。 FindNumber
方法會以傳址方式傳回第一個數字,而此數字大於或等於傳遞為引數的數字。 如果數字未大於或等於引數,則方法會在索引 0 傳回數字。
using System;
class NumberStore
{
int[] numbers = { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 };
public ref int FindNumber(int target)
{
for (int ctr = 0; ctr < numbers.Length; ctr++)
{
if (numbers[ctr] >= target)
return ref numbers[ctr];
}
return ref numbers[0];
}
public override string ToString() => string.Join(" ", numbers);
}
下列範例會呼叫 NumberStore.FindNumber
方法來擷取大於或等於 16 的第一個值。 呼叫者接著會將方法所傳回的值加倍。 範例輸出顯示 NumberStore
執行個體之陣列項目值中所反映的變更。
var store = new NumberStore();
Console.WriteLine($"Original sequence: {store.ToString()}");
int number = 16;
ref var value = ref store.FindNumber(number);
value *= 2;
Console.WriteLine($"New sequence: {store.ToString()}");
// The example displays the following output:
// Original sequence: 1 3 7 15 31 63 127 255 511 1023
// New sequence: 1 3 7 15 62 63 127 255 511 1023
如果不支援參考傳回值,則是透過傳回陣列項目和其值的索引來執行這類作業。 呼叫者接著可以使用這個索引,來修改不同方法呼叫中的值。 不過,呼叫者也可以修改要存取的索引,也可能修改其他陣列值。
下列範例示範如何 FindNumber
重寫 方法,以使用 ref 本機重新指派:
using System;
class NumberStore
{
int[] numbers = { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 };
public ref int FindNumber(int target)
{
ref int returnVal = ref numbers[0];
var ctr = numbers.Length - 1;
while ((ctr >= 0) && (numbers[ctr] >= target))
{
returnVal = ref numbers[ctr];
ctr--;
}
return ref returnVal;
}
public override string ToString() => string.Join(" ", numbers);
}
這個第二個版本在搜尋的數目較接近陣列結尾的案例中,使用較長的序列會更有效率,因為陣列從結尾到開頭逐一查看,因而會檢查較少的專案。
編譯器會在變數上 ref
強制執行範圍規則: ref
類型中的 ref struct
區域變數 ref
、參數和 ref
欄位。 這些規則可確保參考不會與參考的物件不一致。 請參閱 方法參數一文中的範圍規則一節。
ref 和 readonly
修飾 readonly
詞可以套用至 ref
區域變數和 ref
欄位。 readonly
修飾詞會影響右邊的運算式。 請參閱下列範例宣告:
ref readonly int aConstant; // aConstant can't be value-reassigned.
readonly ref int Storage; // Storage can't be ref-reassigned.
readonly ref readonly int CantChange; // CantChange can't be value-reassigned or ref-reassigned.
- value reassignment 表示已重新指派變數的值。
- ref 指派 表示變數現在參考不同的 物件。
readonly ref
和 readonly ref readonly
宣告只在 中的 ref struct
欄位上 ref
有效。
scoped ref
內容關鍵字 scoped
會限制值的存留期。 scoped
修飾詞會將ref-safe-to-escape或safe-to-escape存留期分別限制為目前的 方法。 實際上,新增 scoped
修飾詞會判斷提示您的程式碼不會延長變數的存留期。
您可以套用 scoped
至參數或區域變數。 當 scoped
型別為 ref struct
時,修飾詞可以套用至參數和區域變數。 否則, scoped
修飾詞只能套用至 ref 類型的區域變數。 這包括以 ref
修飾詞宣告的區域變數,以及使用 in
或 ref
out
修飾詞宣告的參數。
當 scoped
型別為 ref struct
時,修飾詞會隱含地新增至 this
、 out
參數和 ref
參數中 struct
宣告的方法。