Ref keyword
ขวัญปีใหม่ พึ่งไชย 650710531
ในที่นี้เราจะมาพูดถึง ref
keyword ในภาษา C# เราจะใช้ตัวแปร ref
เพื่อส่งค่าไปยังเมธอดหรือรับค่าจากเมธอด ซึ่งหมายความว่า เมธอดสามารถเปลี่ยนแปลงค่าของพารามิเตอร์ได้โดยตรงเมื่อแก้ไขค่าที่มีการจองพื้นที่อยู่ก่อนแล้ว เมื่อใช้ ref
การแก้ไขค่าภายในเมธอดจะส่งผลต่อค่าจริงในหน่วยความจำทันที ทำให้ไม่ต้องคัดลอกข้อมูล ช่วยให้โค้ดทำงานเร็วขึ้น โดยเฉพาะกับข้อมูลขนาดใหญ่
📄 การใช้งาน ref
ref
การประกาศ
ต้องประกาศตัวแปร
ref
ไว้ด้านหน้าพารามิเตอร์ของเมธอดต้องประกาศตัวแปร
ref
ไว้ด้านหน้าตัวแปรที่ประกาศไว้ตอนการเรียกใช้เมธอด
การเปลี่ยนแปลงค่า
ถ้าส่งพารามิเตอร์เข้าไปในเมธอดด้วย
ref
เมธอดนั้นจะสามารถเปลี่ยนแปลงค่าของตัวแปรต้นฉบับได้โดยตรง
💻 การใช้ ref
ref
สามารถใช้ ref
ได้ในกรณีต่อไปนี้:
1. ส่งอาร์กิวเมนต์ไปยังเมธอดโดยการอ้างอิงถึงตัวแปรนั้น
(To pass an argument to a method by its reference) ซึ่งการใช้ ref
จะอนุญาตให้เมธอดนั้นแก้ไขค่าของตัวแปรต้นฉบับได้
using System;
namespace TomAndJerryGame
{
class Program
{
static void Main(string[] args)
{
//กำหนดคะแนนเริ่มต้น
int tomScore = 50;
int jerryScore = 45;
Console.WriteLine($"Initial scores: Tom = {tomScore}, Jerry = {jerryScore}");
ModifyScores(ref tomScore, ref jerryScore);
Console.WriteLine($"Modified scores: Tom = {tomScore}, Jerry = {jerryScore}");
}
// ฟังก์ชัน ModifyScores สำหรับอัปเดตคะแนน โดยรับออบเจ็กต์ Character แบบอ้างอิง (ref)
public static void ModifyScores(ref int tomScore, ref int jerryScore)
{
tomScore += 15;
jerryScore += 10;
}
}
}
Output
Initial scores: Tom = 50, Jerry = 45
Modified scores: Tom = 65, Jerry = 55
2. กำหนดลายเซ็นของเมธอดให้ส่งคืนอ้างอิงของตัวแปร
(To define a method signature to return a reference of the variable) การทำให้เมธอดสามารถส่งคืนตำแหน่งในหน่วยความจำของตัวแปรได้แทนที่จะส่งคืนแค่ค่าของตัวแปรนั้น
using System;
namespace TomAndJerryGame
{
class Program
{
static void Main(string[] args)
{
int[] scores = { 20, 50, 80, 100 };
// ใช้ ref เพื่ออ้างอิงไปยังคะแนนที่ต้องการ (50) และรับการอ้างอิงกลับมา
ref int tomScore = ref FindScore(ref scores, 50);
Console.WriteLine("Tom's current score found: " + tomScore);
tomScore += 20;
Console.WriteLine("Tom's updated score: " + tomScore);
Console.WriteLine("\nUpdated Scores:");
foreach (var score in scores)
{
Console.WriteLine(score);
}
}
// ฟังก์ชัน FindScore สำหรับหาคะแนน โดยรับออบเจ็กต์ Character แบบอ้างอิง (ref) 1 ตัว
public static ref int FindScore(ref int[] scores, int target)
{
for (int i = 0; i < scores.Length; i++)
{
if (scores[i] == target)
{
Console.WriteLine("Score found! Updating Tom's score...");
return ref scores[i];
}
}
throw new InvalidOperationException("Score not found.");
}
}
}
Output
Score found! Updating Tom's score...
Tom's current score found: 50
Tom's updated score: 70
Updated Scores:
20
70
80
100
3. ประกาศโครงสร้างเป็น ref struct
ref struct
(To declare a struct as a ref struct) การประกาศ ref struct
จะช่วยให้มั่นใจว่าโครงสร้างนี้จะถูกเก็บใน stack เท่านั้น การที่โครงสร้างอยู่ใน stack จะช่วยลดภาระในการจัดการหน่วยความจำและทำให้การเข้าถึงข้อมูลรวดเร็วขึ้น
using System;
namespace TomAndJerryGame
{
// กำหนด struct แบบ ref struct ที่จะจำกัดให้ถูกใช้งานเฉพาะกับ stack memory เท่านั้น
public ref struct TomJerryPosition
{
public int TomX, TomY;
public int JerryX, JerryY;
public TomJerryPosition(int tomX, int tomY, int jerryX, int jerryY)
{
TomX = tomX;
TomY = tomY;
JerryX = jerryX;
JerryY = jerryY;
}
public void DisplayPositions(string message)
{
Console.WriteLine(message);
Console.WriteLine($"Tom is at ({TomX}, {TomY})");
Console.WriteLine($"Jerry is at ({JerryX}, {JerryY})");
}
}
class Program
{
static void Main()
{
TomJerryPosition positions = new TomJerryPosition(10, 20, 30, 40);
positions.DisplayPositions("Initial Positions:");
positions.TomX += 5;
positions.JerryY -= 10;
positions.DisplayPositions("Updated Positions:");
}
}
}
Output
Initial Positions:
Tom is at (10, 20)
Jerry is at (30, 40)
Updated Positions:
Tom is at (15, 20)
Jerry is at (30, 30)
4. การอ้างอิงในท้องถิ่น
(As local reference) การสร้างตัวแปรที่เชื่อมโยงกับตัวแปรอื่นภายในพื้นที่เดียวกัน ถ้าเราเปลี่ยนแปลงค่าของตัวแปรที่เชื่อมโยง ตัวแปรเดิมก็จะเปลี่ยนไปด้วย
using System;
namespace TomAndJerryGame
{
class Program
{
static void Main(string[] args)
{
// สร้างออบเจ็กต์ของตัวละคร Tom และ Jerry พร้อมคะแนนเริ่มต้น
Character tom = new Character("Tom", 30);
Character jerry = new Character("Jerry", 40);
Console.WriteLine("Before change:");
tom.Display();
jerry.Display();
// ใช้ ref เพื่ออ้างอิงถึงออบเจ็กต์ของ Tom โดยตรง
ref Character activeCharacter = ref tom;
activeCharacter.Score += 20;
Console.WriteLine("After changing Tom's score through reference:");
tom.Display();
jerry.Display();
}
}
class Character
{
public string Name { get; set; }
public int Score { get; set; }
public Character(string name, int score)
{
Name = name;
Score = score;
}
public void Display()
{
Console.WriteLine($"{Name} has a score of {Score}");
}
}
}
Output
Before change:
Tom has a score of 30
Jerry has a score of 40
After changing Tom's score through reference:
Tom has a score of 50
Jerry has a score of 40
⚠️ ข้อควรระวัง
ต้องกำหนดค่าเริ่มต้น: ตัวแปรที่ส่งเข้าไปโดยใช้
ref
ต้องมีค่าเริ่มต้นก่อน ไม่งั้นจะเกิดปัญหาต้องใช้คำสำคัญในทั้งสองที่: เมื่อเราประกาศพารามิเตอร์ในเมธอดก็ต้องใช้
ref
และเมื่อเราจะเรียกใช้เมธอดก็ต้องใส่ref
ด้วยเช่นกัน
การใช้
ref
ใน C# ช่วยให้เราสามารถส่งพารามิเตอร์โดยอ้างอิง พร้อมเปลี่ยนแปลงค่าตัวแปรต้นฉบับได้ เพิ่มประสิทธิภาพในการจัดการข้อมูลขนาดใหญ่ได้เร็วขึ้น
🆚 เปรียบเทียบกับภาษา Java/C/Python
การใช้คีย์เวิร์ด ref
ใน C# เป็นการส่งผ่านค่าผ่านการอ้างอิง (pass by reference) ซึ่งมีความแตกต่างจากการส่งผ่านค่าปกติ (pass by value) ที่เป็นการส่งค่าจากตัวแปรไปยังฟังก์ชันหรือเมธอดอื่น ๆ ในรูปแบบที่ค่าตัวแปรนั้นถูกส่งตรงตามที่อยู่ในหน่วยความจำ ไม่ใช่ค่าสำเนาของตัวแปรนั้น ๆ
การทำงานของ ref
ใน C#
ref
ใน C#ใน C# การใช้
ref
จำเป็นต้องใช้ทั้งในตอนที่เรียกใช้และตอนที่ประกาศฟังก์ชัน เพื่อบอกว่าอาร์กิวเมนต์ถูกส่งผ่านโดยการอ้างอิงเมื่อส่งตัวแปรไปยังฟังก์ชันด้วย
ref
การเปลี่ยนแปลงในตัวแปรภายในฟังก์ชันจะสะท้อนผลกลับมายังตัวแปรที่ส่งไป
using System;
class Program
{
static void UpdateValue(ref int number)
{
number = 20;
}
static void Main()
{
int value = 10;
UpdateValue(ref value);
Console.WriteLine(value);
}
}
Output
20
การเปรียบเทียบกับภาษาอื่น ๆ
1. Java
เมื่อเราส่งผ่านการอ้างอิง (pass by reference) หมายความว่า เราส่ง "ตัวจริง" ของออบเจ็กต์เข้าไปในฟังก์ชัน ไม่ได้ส่งสำเนา เมื่อฟังก์ชันทำอะไรกับออบเจ็กต์นั้น เช่น เปลี่ยนค่าในออบเจ็กต์ ค่าที่ถูกเปลี่ยนจะมีผลกับตัวจริงเลย เพราะทั้งตัวที่ส่งเข้าไปและตัวที่ใช้ในฟังก์ชันต่างก็ชี้ไปที่ออบเจ็กต์เดียวกัน ไม่ได้แยกกันทำงาน
ดังนั้น ถ้าเราส่งออบเจ็กต์ไปฟังก์ชันแล้วไปเปลี่ยนค่าอะไรในนั้น ค่าในตัวจริงก็จะเปลี่ยนตามไปด้วย
เพราะฉะนั้น ใน Java ไม่มีคีย์เวิร์ดเฉพาะแบบ ref
เพราะ Java ใช้การส่งค่าตัวแปรแบบ pass-by-value ในทุกกรณี แต่หากส่งออบเจ็กต์จะเป็นการส่งที่อยู่ของออบเจ็กต์นั้น (pass-by-reference) คล้ายกับการใช้งาน ref
ใน C#
import java.io.*;
class updateValue {
int Number;
void updateValue() {
Number = 0;
}
static void update(updateValue ob) {
ob.Number++;
}
public static void main(String[] args)
{
updateValue ob = new updateValue();
System.out.println("Number value " + (ob.Number));
update(ob);
System.out.println("Updated Number value " + (ob.Number));
}
}
Output
Number value 0
Updated Number value 1
2. C
ในภาษา C สามารถทำสิ่งที่คล้ายกับ ref
ได้ด้วยการใช้ pointer (ตัวชี้) ซึ่งเป็นการส่งตัวแปรผ่านการอ้างอิงที่ตรงไปตรงมา
#include <stdio.h>
void updateValue(int* number) {
*number = 20;
}
int main() {
int value = 10;
updateValue(&value);
printf("%d\n", value);
return 0;
}
Output
20
3. Python
Python ส่งผ่านค่าตัวแปรแบบ pass-by-object-reference ซึ่งมีลักษณะคล้ายกับการ pass-by-reference ใน C# เมื่อทำการส่งออบเจ็กต์ (เช่น list หรือ dictionary) แต่หากส่งค่าที่เป็น immutable (เช่น integer, string) จะคล้ายกับ pass-by-value
รูปแบบการส่งอาร์กิวเมนต์ของ Python ไม่ใช่ “Pass by Value” หรือ “Pass by Reference” แต่เป็น “Pass by Object Reference”
def update_value(my_list):
my_list[0] = 50
numbers = [10, 20, 30]
print("before update : ", numbers)
update_value(numbers)
print("after update : ", numbers)
Output
before update : [10, 20, 30]
after update : [50, 20, 30]
สรุป
C#: ใช้
ref
สำหรับ pass-by-reference ทั้งการประกาศและการเรียกฟังก์ชันJava: ไม่มี
ref
โดยตรง แต่การส่งออบเจ็กต์มีลักษณะคล้ายการ pass-by-referenceC: ใช้ pointer เพื่อส่งผ่านค่าผ่านการอ้างอิง
Python: ใช้การส่งผ่านออบเจ็กต์ (pass-by-object-reference) ขึ้นอยู่กับชนิดข้อมูล
การใช้
ref
ทำให้ควบคุมการส่งผ่านค่าผ่านการอ้างอิงได้ชัดเจนขึ้นใน C#
🎞️Presentation
📚 แหล่งที่มา
1. GeeksforGeeks. (July 16, 2021). Ref in C#.
Retrieved from https://www.geeksforgeeks.org/ref-in-c-sharp/
2. Microsoft. (January 8, 2024). Ref keyword.
Retrieved from https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ref
3. TutorialsTeacher. (January 7, 2020). Ref keyword.
Retrieved from https://www.tutorialsteacher.com/articles/ref-keyword
4. Microsoft. (July 30, 2024). Ref struct.
Retrieved fromhttps://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/ref-struct
GeeksforGeeks. (October 17, 2022). Different Ways to Achieve Pass By Reference in Java.
Retrieved from https://www.geeksforgeeks.org/different-ways-to-achieve-pass-by-reference-in-java/
GeeksforGeeks. (July 24, 2024). C Pointers.
Retrieved from https://www.geeksforgeeks.org/c-pointers/
GeeksforGeeks. (August 9, 2024). Pass by reference vs value in Python.
Retrieved from https://www.geeksforgeeks.org/pass-by-reference-vs-value-in-python/
Last updated