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

Truyền tham số cho phương thức trong C# (Pass by Value / Reference)

C# hỗ trợ 4 cơ chế truyền tham số:

Cơ chếTừ khóaMô tảValue TypeReference 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ốcCopy reference, thay đổi object ảnh hưởng gốc, gán lại parameter không ảnh hưởng gốc
Pass by ReferencerefTruyền địa chỉ biến, thay đổi ảnh hưởng biến gốcThay đổi giá trị gốcThay đổi object GẮN LẠI reference cũng ảnh hưởng gốc
Out ParameteroutGiống ref nhưng BẮT BUỘC gán giá trị trước khi returnPhải gánPhải gán
In ParameterinRead-only reference, tránh copy large structRead-only, compiler enforceRead-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 ref khi CẦN thay đổi biến caller (swap, out-like pattern cũ).
  • Dùng out cho TryParse pattern, multiple return values.
  • Dùng in cho large readonly struct (> 16 bytes) để tránh copy.
  • Tránh ref/out trên reference type trừ khi cần reassign variable.
  • C# 7+: out var x inline declaration.