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

ความแตกต่างระหว่าง Pass by Value และ Pass by Reference ใน C#

การส่งพารามิเตอร์ใน C#: Pass by Value vs Pass by Reference

C# รองรับการส่งพารามิเตอร์ 2 แบบหลัก ส่งผลต่อการที่เมธอดจะแก้ไขค่าตัวแปรต้นฉบับได้หรือไม่

1. Pass by Value (ส่งค่า) - ค่าเริ่มต้น

  • สร้าง **สำเนา** ของค่า (copy) ให้เมธอด
  • เมธอดทำงานบนสำเนา **ไม่ส่งผลต่อตัวแปรต้นฉบับ
  • ใช้กับ Value Types (struct, int, bool, double, etc.) โดยอัตโนมัติ

using System;

class Demo
{
    static void ModifyValue(int x)
    {
        x = 20; // แก้ไขแค่สำเนา
        Console.WriteLine($"ในเมธอด: x = {x}");
    }

    static void Main()
    {
        int value = 10;
        Console.WriteLine($"ก่อนเรียก: value = {value}"); // 10
        ModifyValue(value);
        Console.WriteLine($"หลังเรียก: value = {value}"); // 10 (ไม่เปลี่ยน!)
    }
}

2. Pass by Reference (ส่งอ้างอิง) - ใช้ ref/out

  • ส่ง **ที่อยู่หน่วยความจำ** (reference) ของตัวแปร
  • เมธอดเข้าถึงและ **แก้ไขตัวแปรต้นฉบับได้จริง**
  • ต้องใช้ keyword ref หรือ out ทั้ง declaration และการเรียก

using System;

class Demo
{
    // ref: ต้อง initialize ก่อนส่ง
    static void ModifyRef(ref int x)
    {
        x = 20;
        Console.WriteLine($"ในเมธอด ref: x = {x}");
    }

    // out: ไม่ต้อง initialize ก่อน ส่งค่ากลับบังคับ
    static void ModifyOut(out int x)
    {
        x = 30; // ต้อง assign ก่อนออกจากเมธอด
        Console.WriteLine($"ในเมธอด out: x = {x}");
    }

    static void Main()
    {
        int value = 10;
        Console.WriteLine($"ก่อน ref: {value}"); // 10
        ModifyRef(ref value);
        Console.WriteLine($"หลัง ref: {value}");  // 20 (เปลี่ยน!)

        int uninitialized;
        ModifyOut(out uninitialized);
        Console.WriteLine($"หลัง out: {uninitialized}"); // 30
    }
}

ตารางเปรียบเทียบ ref vs out

คุณสมบัติrefout
ต้อง initialize ก่อนส่งใช่ไม่ (แต่ต้อง assign ในเมธอด)
วัตถุประสงค์Input/Output (อ่านและเขียน)Output เท่านั้น (เขียนคืนค่า)
อ่านค่าได้ก่อน assignได้ไม่ได้ (compile error)
ใช้กับ TryParse patternไม่ใช่ (มาตรฐาน)

Reference Types (Class, Array, String) - พฤติกรรมพิเศษ


class Person { public string Name; }

void ChangeName(Person p)
{
    p.Name = "Changed";     // ✅ เปลี่ยน property ได้ (เข้าถึง object เดียวกัน)
    p = new Person();       // ❌ เปลี่ยน reference local ไม่ส่งผลนอก
}

void ChangeNameRef(ref Person p)
{
    p = new Person { Name = "New" }; // ✅ เปลี่ยน reference ต้นฉบับได้
}

สรุป: เมื่อไหร่ใช้อะไร?

สถานการณ์แนะนำ
Value type (int, struct) - อ่านอย่างเดียวPass by value (default)
Value type - ต้องแก้ไขค่าต้นฉบับref หรือ out
Value type - คืนค่าหลายค่าout parameters หรือ Tuple
Reference type - แก้ไข property/fieldPass by value (default) พอ
Reference type - ต้องเปลี่ยน reference เองref
Array/List - เพิ่ม/ลบ/แก้ไข elementPass by value พอ (reference ถูกคัดลอก)

C# 7+ Tuples - ทางเลือกแทน out parameters


// แทนที่ out parameters
(int sum, int count) Calculate(int[] arr)
{
    int sum = 0;
    foreach (var n in arr) sum += n;
    return (sum, arr.Length);
}

var (total, cnt) = Calculate(new[] { 1, 2, 3 });