Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C#

ความแตกต่างระหว่าง out และ ref ใน C#

ความแตกต่างสรุป

คุณสมบัติrefout
ทิศทางข้อมูลIn/Out (Two-way)Out Only (One-way)
Initialization ก่อนส่งบังคับ ต้อง Initialize ก่อนไม่ต้อง Initialize
Assignment ใน MethodOptional (อ่าน/เขียนได้)บังคับ ต้อง Assign ทุก Path
วัตถุประสงค์Modify Existing VariableReturn Additional Values
Overload Resolutionแยกจาก outแยกจาก ref

ref - Reference Parameter


void Modify(ref int x)
{
    Console.WriteLine($"Before: {x}"); // อ่านค่าเดิมได้
    x = 20; // แก้ไขค่า Caller
}

int a = 10; // ต้อง Initialize
Modify(ref a);
Console.WriteLine($"After: {a}"); // 20

out - Output Parameter


void GetValues(out int x, out int y)
{
    // x, y ยังไม่มีค่า (Unassigned)
    // Console.WriteLine(x); // ❌ Error: Use of unassigned out parameter
    
    x = 10; // บังคับ Assign
    y = 20; // บังคับ Assign
}

GetValues(out int a, out int b); // ไม่ต้อง Initialize ก่อน
Console.WriteLine($"{a}, {b}"); // 10, 20

ตัวอย่าง Try Pattern (out)


bool TryParseInt(string input, out int result)
{
    return int.TryParse(input, out result);
}

// Usage
if (TryParseInt("123", out int value))
{
    Console.WriteLine($"Parsed: {value}");
}
else
{
    Console.WriteLine("Invalid number");
}

ตัวอย่าง ref (Swap)


void Swap(ref int a, ref int b)
{
    int temp = a;
    a = b;
    b = temp;
}

int x = 1, y = 2;
Swap(ref x, ref y);
Console.WriteLine($"{x}, {y}"); // 2, 1

ref return & ref local (C# 7+)


int[] numbers = { 1, 2, 3, 4, 5 };

// Return Reference to Array Element
ref int FindFirstEven(int[] arr)
{
    for (int i = 0; i < arr.Length; i++)
    {
        if (arr[i] % 2 == 0)
            return ref arr[i]; // Return Reference
    }
    throw new InvalidOperationException();
}

// Usage
ref int evenRef = ref FindFirstEven(numbers);
evenRef = 100; // Modify Array Element Directly!
Console.WriteLine(numbers[1]); // 100

in Parameter (C# 7.2+) - Read-only Reference


void ReadOnly(in BigStruct data)
{
    // data.X = 10; // ❌ Compile Error: Cannot assign to 'in' parameter
    Console.WriteLine(data.X); // ✅ Read OK
}

// ใช้กับ Struct ใหญ่ หลีกเลี่ยง Copy
readonly struct BigStruct
{
    public long A, B, C, D, E, F, G, H;
}

void Process(in BigStruct s) { } // Pass by Ref, No Copy

Method Overloading: ref vs out


class Demo
{
    public void Method(ref int x) { } // Overload 1
    public void Method(out int x) { } // Overload 2 (Valid - Different Signature)
    
    // public void Method(int x) { } // Overload 3 (Valid)
}

// Call
int a = 0;
demo.Method(ref a); // Calls ref version
demo.Method(out a); // Calls out version

Best Practice

  • ✅ ใช้ out สำหรับ Try Pattern, Multiple Return Values
  • ✅ ใช้ ref สำหรับ Swap, Modify Caller Variable, Performance (Large Struct)
  • ✅ ใช้ in สำหรับ Read-only Large Struct Parameter
  • ✅ ใช้ Tuple/Record แทน out/ref สำหรับ General Multiple Returns
  • ❌ หลีกเลี่ยง ref/out ใน Public API (Complexity, Confusion)
  • ❌ อย่าใช้ ref/out กับ Async Methods (ไม่รองรับ)