簡單來認識in、ref、out、params的差異
params 指定該參數可以接受可變數目的引數。
in 指定該參數是傳址方式傳遞,但只會由所呼叫的方法讀取。
ref 指定該參數是傳址方式傳遞,且可以由所呼叫的方法讀取或寫入。
out 指定該參數是傳址方式傳遞,且由所呼叫的方法寫入。
Method Parameters - in
in關鍵字會使參數以傳址方式傳遞,但可確保不會修改參數。 所以在傳遞in修飾的參數時,需要經過初始化才可以使用。 它就像 ref 或 out 關鍵字,不同的是 in 引數不能被呼叫的方法修改。
void InArgExample(in int number)
{
// Uncomment the following line to see error CS8331
//number = 19;
}
int readonlyArgument = 44;
InArgExample(readonlyArgument);
Console.WriteLine(readonlyArgument); // value is still 44
上述為官方的範例, 若使用in修飾的參數其方法無法修改變數內容,若修改參數將會出現以下錯誤。
CS8331: Cannot assign to variable ‘in int’ because it is a readonly variable
使用時機
當您新增 in 修飾詞來利用參考傳遞引數時,即表明您的設計目的是利用參考傳遞引數,來避免不必要的複製。
其他應用
另外in還有以下幾種應用
- 泛型介面和委派的泛型型別參數。
- foreach 語句。
- LINQ 查詢運算式中的 from 子句。
- LINQ 查詢運算式中的 join 子句。
Method Parameters - ref
ref 指定該參數是傳址方式傳遞,且可以由所呼叫的方法讀取或寫入, 另外方法定義和呼叫方法都必須明確使用 ref 關鍵字,官方範例如下:
void Method(ref int refArgument)
{
refArgument = refArgument + 44;
}
int number = 1;
Method(ref number);//呼叫時也要記得使用ref關鍵字
Console.WriteLine(number);
// Output: 45
使用時機
例如,假設呼叫端傳遞區域變數運算式或陣列元素存取運算式。 接著,呼叫的方法可以取代 ref 參數所參考的物件。 在此情況下,呼叫端的區域變數或陣列專案會在方法傳回時參考新的物件。
Method Parameters - out
out指定該參數是傳址方式傳遞,且由所呼叫的方法寫入,故不用先初始化,不過需要先指派值給被呼叫的方法,方法才能傳回。
範例如下:
int initializeInMethod;
OutArgExample(out initializeInMethod);
Console.WriteLine(initializeInMethod); // value is now 44
void OutArgExample(out int number)
{
number = 44;
}
建議
使用 out 引數來宣告方法是傳回多個值的傳統因應措施。 從 C# 7.0 開始,官方考慮使用 Tuple。
Method Parameters - params
使用 params 關鍵字的特性:
- 可變長度的引數
- 必須是一維陣列
- params關鍵字後面不允許任何其他參數
- 只允許一個 params 關鍵字
- 如果參數的宣告型 params 別不是一維陣列,就會發生編譯器錯誤
當您使用 params 參數呼叫方法時,您可以傳入:
- 陣列元素型別的引數清單(以逗號分隔)。
- 指定之類型的引數陣列。
- 無引數。 如果不傳送任何引數,params 清單的長度為零。
官方範例如下:
public class MyClass
{
public static void UseParams(params int[] list)
{
for (int i = 0; i < list.Length; i++)
{
Console.Write(list[i] + " ");
}
Console.WriteLine();
}
public static void UseParams2(params object[] list)
{
for (int i = 0; i < list.Length; i++)
{
Console.Write(list[i] + " ");
}
Console.WriteLine();
}
static void Main()
{
// You can send a comma-separated list of arguments of the
// specified type.
UseParams(1, 2, 3, 4);
UseParams2(1, 'a', "test");
// A params parameter accepts zero or more arguments.
// The following calling statement displays only a blank line.
UseParams2();
// An array argument can be passed, as long as the array
// type matches the parameter type of the method being called.
int[] myIntArray = { 5, 6, 7, 8, 9 };
UseParams(myIntArray);
object[] myObjArray = { 2, 'b', "test", "again" };
UseParams2(myObjArray);
// The following call causes a compiler error because the object
// array cannot be converted into an integer array.
//UseParams(myObjArray);
// The following call does not cause an error, but the entire
// integer array becomes the first element of the params array.
UseParams2(myIntArray);
}
}
/*
Output:
1 2 3 4
1 a test
5 6 7 8 9
2 b test again
System.Int32[]
*/
差異整理
- in、ref的參數需初始化,out不用初始化。
- ref、out可以在呼叫時進行參數的寫入、in僅唯讀。
- 擴充方法 具有下列限制:
out關鍵字不能用在擴充方法的第一個引數上。 ref當引數不是struct,或是泛型型別不受限制為struct時,無法在擴充方法的第一個引數上使用 關鍵字。 in除非第一個引數是struct,否則無法使用 關鍵字。 in關鍵字不能用於任何泛型型別,即使限制為 struct也一樣。
該篇參考官方Method Parameters