C# 中 ref、out 和 in 關鍵字的用法是什麼?


在 C# 中,大多數方法可以有零個或多個引數,這些引數定義了必須提供給方法的資料。任何呼叫該方法的程式碼都必須將資料(稱為引數)傳遞給該方法。方法將輸入宣告為引數,並且這些引數由呼叫程式碼以引數的形式提供。

例如,考慮以下方法和後續方法呼叫。

static void Greet(string greeting){
   Console.WriteLine(greeting);
}
...
Greet("Hello");

在上面的示例中,greeting 是 Greet() 方法的引數,“Hello” 是傳遞給方法的引數。

當您呼叫方法並傳遞引數時,它們是按值傳遞的,這意味著在傳遞給方法時會建立值的副本。在方法內部對引數所做的任何更改都不會反映到原始變數上。

using System;
int number = 8;
Increase(number);
Console.WriteLine(number); // prints 8
static void Increase(int num){
   num = num + 1;
   Console.WriteLine(num); // prints 9
}

當您傳遞引用型別變數(例如物件)時,C# 仍然按值複製**引用**,因為變數儲存的是引用,而不是實際的物件。因此,即使複製了引用變數的副本,它們也都會引用記憶體中的同一個物件。因此,方法內部變數對物件所做的任何更改都對方法外部的變數可見。

using System;
var john = new User{
   Name = "John",
   Salary = 50000
};
Promote(john);
Console.WriteLine(john.Salary); // prints 60000
static void Promote(User u){
   u.Salary += 10000;
   Console.WriteLine(u.Salary); // prints 60000
}

但是,如果您在方法內部更改變數本身的值,則該更改不會反映到方法外部,因為只更改了副本,而不是實際的物件。例如,我們可以在方法內部將引數賦值為 null,而實際變數將保持不變。

using System;
var john = new User{
   Name = "John",
   Salary = 50000
};
Promote(john);
Console.WriteLine(john.Salary); // prints 50000
static void Promote(User u){
   u = null;
}

C# 允許使用三個不同的修飾符關鍵字來控制方法的引數。

ref 修飾符

C# 允許您使用ref修飾符按引用傳遞引數。傳遞的變數是引用型別還是值型別都無關緊要。引數和引數都將引用同一個記憶體位置。

在下面的示例中,將引數 u 設定為 null 也會使 john 變數為 null,從而導致空引用異常。

示例

using System;
var john = new User{
   Name = "John",
   Salary = 50000
};
Promote(ref john);
Console.WriteLine(john.Salary); // throws System.NullReferenceException
static void Promote(ref User u){
   u = null;
}

使用 ref 修飾符傳遞的引數必須在傳遞之前進行初始化。

out 修飾符

它類似於 ref 修飾符,除了

  • 引數不必在進入函式之前進行初始化

  • 引數必須在退出函式之前進行初始化(賦值)。

在下面的示例中,Hire 函式初始化一個新的使用者物件,該物件透過 out 修飾符傳遞給它。請注意,在呼叫 Hire 方法時,變數 john 是即時宣告的。

using System;
Hire(out User john);
Console.WriteLine(john.Salary); // prints 50000
static void Hire(out User u){
   u = new User{
      Name = "John",
      Salary = 50000
   };
}

ref修飾符一樣,用out修飾符標記的變數是按引用傳遞的。

通常,out變數用於從函式獲取多個返回值,如下所示:

using System;
var john = new User{
   Name = "John",
   Salary = 50000
};
bool shouldPromote = Raise(john.Salary, out double hike);
Console.WriteLine(shouldPromote); // True
Console.WriteLine($"Hike Amount = {hike}"); // prints 5000
static bool Raise(int salary, out double hike){
   hike = salary * 0.1;
   return hike < 7000;
}

in 修飾符

它類似於refout引數,除了接受in引數值的方法不能修改引數。如果嘗試修改,C# 編譯器會生成編譯時錯誤。

in引數可以防止編譯器將大型值型別的記憶體複製到引數變數,同時防止物件意外修改。這使得它們在將大型值型別傳遞給方法時非常有用。

var point = new Coord();
Verify(point);
static void Verify(in Coord c){
   // error: Cannot assign to variable 'in Coord' because it is a readonly variable
   c = new Coord();
}
struct Coord{
   public int X;
   public int Y;
}

示例

 即時演示

using System;
class Program{
   static void Main(){
      int number = 8;
      Increase(number);
      Console.WriteLine(number); // prints 8
      var john = new User { Name = "John", Salary = 50000 };
      Promote(john);
      Console.WriteLine(john.Salary); // prints 60000
      Leave(john);
      Console.WriteLine(john.Salary); // prints 60000

      LeaveByRef(ref john);
      Console.WriteLine(john?.Salary); // prints nothing

      User dave;
      Hire(out dave);
      Console.WriteLine(dave.Salary); // prints 50000
   }
   static void Increase(int num){
      num = num + 1;
      Console.WriteLine(num); // prints 9
   }
   static void Promote(User u){
      u.Salary += 10000;
      Console.WriteLine(u.Salary); // prints 60000
   }
   static void Leave(User u){
      u = null;
   }
   static void LeaveByRef(ref User u){
      u = null;
   }
   static void Hire(out User u){
      u = new User{
         Name = "John",
         Salary = 50000
      };  
   }
}
class User{
   public string Name { get; set; }
   public int Salary { get; set; }
}

輸出

9
8
60000
60000
60000
50000

更新時間: 2021年5月19日

456 次瀏覽

開啟你的 職業生涯

透過完成課程獲得認證

立即開始
廣告