C# hỗ trợ 4 cơ chế truyền tham số:
| Cơ chế | Từ khóa | Mô tả | Value Type | Reference Type |
|---|---|---|---|---|
| Pass by Value (Mặc định) | (không có) | Copy giá trị (value type) hoặc copy reference (reference type) | Tạo bản copy, thay đổi không ảnh hưởng gốc | Copy reference, thay đổi object ảnh hưởng gốc, gán lại parameter không ảnh hưởng gốc |
| Pass by Reference | ref | Truyền địa chỉ biến, thay đổi ảnh hưởng biến gốc | Thay đổi giá trị gốc | Thay đổi object GẮN LẠI reference cũng ảnh hưởng gốc |
| Out Parameter | out | Giống ref nhưng BẮT BUỘC gán giá trị trước khi return | Phải gán | Phải gán |
| In Parameter | in | Read-only reference, tránh copy large struct | Read-only, compiler enforce | Read-only reference |
Ví dụ 1: Pass by Value (Mặc định)
using System;
class Program
{
static void SwapByValue(int x, int y)
{
int temp = x; x = y; y = temp; // Chỉ thay đổi local copy
}
static void ModifyObject(StringBuilder sb)
{
sb.Append(" World"); // Thay đổi object được (shared reference)
sb = new StringBuilder("New"); // Gán lại parameter - KHÔNG ảnh hưởng caller
}
static void Main()
{
int a = 50, b = 150;
Console.WriteLine($"Before: a={a}, b={b}");
SwapByValue(a, b);
Console.WriteLine($"After: a={a}, b={b}"); // Không đổi!
var sb = new StringBuilder("Hello");
ModifyObject(sb);
Console.WriteLine(sb); // "Hello World" (object mutated)
}
}
Ví dụ 2: ref - Pass by Reference
static void SwapByRef(ref int x, ref int y)
{
int temp = x; x = y; y = temp; // Thay đổi biến gốc
}
static void ReassignRef(ref StringBuilder sb)
{
sb = new StringBuilder("Reassigned"); // Ảnh hưởng biến caller!
}
static void Main()
{
int a = 10, b = 20;
SwapByRef(ref a, ref b);
Console.WriteLine($"Swapped: a={a}, b={b}"); // a=20, b=10
var sb = new StringBuilder("Original");
ReassignRef(ref sb);
Console.WriteLine(sb); // "Reassigned"
}
Ví dụ 3: out - Output Parameter
static bool TryParseInt(string s, out int result)
{
// BẮT BUỘC gán result trước khi return
return int.TryParse(s, out result);
}
static void GetCoordinates(out int x, out int y)
{
x = 10; // Must assign
y = 20; // Must assign
}
static void Main()
{
if (TryParseInt("123", out int num))
Console.WriteLine($"Parsed: {num}");
GetCoordinates(out int x, out int y);
Console.WriteLine($"({x}, {y})");
}
Ví dụ 4: in - Read-only Reference (C# 7.2+)
struct LargeStruct
{
public long A, B, C, D, E, F, G, H; // 64 bytes
}
static void Process(in LargeStruct data) // Truyền bằng reference, read-only
{
// data.A = 1; // COMPILE ERROR: cannot assign to 'in' parameter
Console.WriteLine(data.A);
}
static void Main()
{
var big = new LargeStruct { A = 42 };
Process(in big); // Không copy 64 bytes, chỉ pass reference
}
Best Practices
- Mặc định: Pass by value (an toàn, rõ ràng).
- Dùng
refkhi CẦN thay đổi biến caller (swap, out-like pattern cũ). - Dùng
outcho TryParse pattern, multiple return values. - Dùng
incho large readonly struct (> 16 bytes) để tránh copy. - Tránh
ref/outtrên reference type trừ khi cần reassign variable. - C# 7+:
out var xinline declaration.