Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Các loại tham số (Parameters) của Method trong C#

Tổng quan 3 loại chính

LoạiTừ khóaTruyềnThay đổi gốc?Khởi tạo trước?
Value(mặc định)Copy giá trịKhôngCó
ReferencerefReference (địa chỉ)CóCó
OutputoutReference (địa chỉ)Có (bắt buộc gán)Không cần

1. Value Parameters (Mặc định)

Truyền bản sao giá trị. Thay đổi trong method không ảnh hưởng biến gốc.


void Modify(int x) 
{
    x = 10; // Chỉ thay đổi local copy
}

int a = 5;
Modify(a);
Console.WriteLine(a); // 5 - không đổi

Áp dụng cho: primitive types (int, bool, double...), struct, enum.

2. Reference Parameters (ref)

Truyền tham chiếu đến biến gốc. Thay đổi trong method ảnh hưởng biến gốc.


void Modify(ref int x) 
{
    x = 10; // Thay đổi biến gốc
}

int a = 5;
Modify(ref a);
Console.WriteLine(a); // 10 - ĐÃ THAY ĐỔI

Yêu cầu: Biến phải khởi tạo trước khi truyền.

3. Output Parameters (out)

Tương tự ref nhưng: không cần init trước, BẮT BUỘC gán giá trị trong method.


bool TryParse(string s, out int result) 
{
    // Phải gán result trên TẤT CẢ code paths
    if (int.TryParse(s, out int value)) 
    {
        result = value;
        return true;
    }
    result = 0; // Bắt buộc
    return false;
}

// Gọi
if (TryParse("123", out int num)) 
{
    Console.WriteLine(num); // 123
}

4. In Parameters (C# 7.2+) - Read-only Reference


// Chỉ đọc, không cho phép sửa - tránh copy large struct
void Process(in BigStruct data) 
{
    // data.X = 10; // ❌ Compile error: cannot assign
    Console.WriteLine(data.Value);
}

// Dùng cho large struct (> 16 bytes) để tránh copy
readonly struct BigStruct 
{
    public long A, B, C, D, E;
    public int Value => (int)(A + B);
}

5. Params (Mảng tham số)


void PrintAll(params string[] names) 
{
    foreach (var n in names) Console.WriteLine(n);
}

// Gọi
PrintAll("A", "B", "C");
PrintAll(new[] { "X", "Y" });

// C# 13+: params Span, params IEnumerable
void PrintAll(params ReadOnlySpan names) { }

6. Optional Parameters (Default Value)


void Greet(string name, string greeting = "Hello") 
{
    Console.WriteLine($"{greeting}, {name}");
}

Greet("John");           // Hello, John
Greet("John", "Hi");     // Hi, John
Greet(name: "John");     // Named argument

7. Ref Return & Ref Local (C# 7+)


// Trả về reference đến element trong array/field
ref int FindFirst(int[] arr, int target) 
{
    for (int i = 0; i < arr.Length; i++) 
    {
        if (arr[i] == target) return ref arr[i];
    }
    throw new ArgumentException();
}

// Sử dụng
int[] numbers = { 1, 2, 3, 4, 5 };
ref int found = ref FindFirst(numbers, 3);
found = 99; // Thay đổi trực tiếp trong array!
Console.WriteLine(numbers[2]); // 99

8. Ref Struct (C# 7.2+) - Stack-only


// Chỉ tồn tại trên stack, không thể boxing, không dùng trong async/iterator
public ref struct SpanWrapper 
{
    public Span Data;
    
    public SpanWrapper(Span data) => Data = data;
}

Best Practices

  1. Default: Value parameters (an toàn, đơn giản).
  2. Large struct (>16 bytes): in parameter tránh copy.
  3. Multiple returns: Tuple (T1, T2) thay vì out/ref.
  4. TryParse pattern: out parameter.
  5. Modify caller variable: ref (ít dùng, cân nhắc return value).
  6. Params: Convenience API, logging, formatting.

Ví dụ tổng hợp


public class Calculator 
{
    // Value - input nhỏ
    public int Add(int a, int b) => a + b;
    
    // In - large struct input
    public double Distance(in Point3D a, in Point3D b) 
        => Math.Sqrt((a.X-b.X)*(a.X-b.X) + (a.Y-b.Y)*(a.Y-b.Y) + (a.Z-b.Z)*(a.Z-b.Z));
    
    // Out - Try pattern
    public bool TryDivide(int a, int b, out int result) 
    {
        if (b == 0) { result = 0; return false; }
        result = a / b;
        return true;
    }
    
    // Ref - swap (hiếm dùng)
    public void Swap(ref int a, ref int b) 
    {
        (a, b) = (b, a); // C# 7+ tuple swap
    }
    
    // Params - flexible args
    public int Sum(params int[] numbers) => numbers.Sum();
    
    // Optional - default
    public int Multiply(int a, int b = 2) => a * b;
}