การส่งพารามิเตอร์ใน 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
| คุณสมบัติ | ref | out |
| ต้อง 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/field | Pass by value (default) พอ |
| Reference type - ต้องเปลี่ยน reference เอง | ref |
| Array/List - เพิ่ม/ลบ/แก้ไข element | Pass 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 });