ข้ามไปยังเนื้อหา

Methods

Method คือฟังก์ชันที่มี receiver — parameter พิเศษที่ปรากฏระหว่าง keyword func กับชื่อฟังก์ชัน receiver ผูกฟังก์ชันนั้นเข้ากับ named type

func (r Rectangle) Area() float64 {
return r.Width * r.Height
}

ที่นี่ r Rectangle คือ receiver Area กลายเป็น method ของ Rectangle เรียกได้ว่า rect.Area()

Receiver สามารถเป็น value หรือ pointer ก็ได้:

func (c Counter) Value() int { return c.count } // value receiver
func (c *Counter) Inc() { c.count++ } // pointer receiver
Value receiver (t T)Pointer receiver (t *T)
เห็นสำเนาของค่าค่าต้นฉบับ
แก้ไขได้?ไม่ได้ — แก้เฉพาะสำเนาได้
เรียกได้จากทั้ง T และ *Tทั้ง T (addressable) และ *T
ใช้เมื่ออ่านอย่างเดียว; ขนาดเล็ก, copy ถูกต้องแก้ไข; struct ใหญ่; method set สม่ำเสมอ

ความสม่ำเสมอ: ถ้า method ใด ๆ บน type ต้องการ pointer receiver ให้ใช้ pointer receiver กับ ทุก method ของ type นั้น การผสมกันทำให้เกิดปัญหา method set ที่ละเอียดอ่อนเมื่อ type ถูกใช้เป็น interface value

Methods ไม่จำกัดแค่ structs คุณนิยาม methods บน named type ใด ๆ ใน package ของคุณได้:

type Celsius float64
func (t Celsius) String() string {
return fmt.Sprintf("%.1f°C", float64(t))
}

คุณไม่สามารถนิยาม method บน type จาก package อื่น (เช่น func (s string) Upper() ผิดกฎ) แต่การห่อ type นั้นใน named type ในท้องถิ่นใช้ได้เสมอ

Method set ของ type กำหนดว่า interfaces ใดที่ตอบสนองได้:

  • Method set ของ T มี methods ทั้งหมดที่มี value receiver (t T)
  • Method set ของ *T มี methods ทั้งหมด — ทั้ง (t T) และ (t *T)

นี่คือเหตุผลที่ interface ที่ต้องการ pointer-receiver method สามารถตอบสนองได้เฉพาะโดย *T ไม่ใช่ T

package main
import "fmt"
type Counter struct {
count int
}
// Pointer receiver — mutates the struct
func (c *Counter) Inc() {
c.count++
}
// Value receiver — read-only
func (c Counter) Value() int {
return c.count
}
type Celsius float64
func (t Celsius) String() string {
return fmt.Sprintf("%.1f\u00b0C", float64(t))
}
func main() {
c := Counter{}
c.Inc()
c.Inc()
c.Inc()
fmt.Println(c.Value())
temp := Celsius(36.6)
fmt.Println(temp.String())
}
สิ่งที่ได้ประโยชน์ต้นทุน
value receiverimmutable, เรียกได้บน non-addressable valuescopy struct ทุกครั้งที่เรียก
pointer receivermutate ได้, ไม่ copy structต้อง nil-check, pointer ต้อง addressable
method บน non-struct typeขยาย behavior ของ any type ใน packageไม่สามารถ add method บน imported type ได้
  • value receiver และ pointer receiver ใช้แทนกันได้ — value receiver ทำงานบนสำเนา ไม่สามารถ mutate receiver ได้
  • สามารถเพิ่ม method บน type ของ package อื่นได้ — Go ไม่อนุญาต ต้อง wrap หรือ embed แทน
  • *method set ของ T และ T เหมือนกัน — *T มี method set ใหญ่กว่า (รวมทั้ง pointer-receiver methods) ส่งผลต่อ interface satisfaction

💡 ตัวอย่างจากของจริง

net/http: http.ResponseWriter เป็น interface — Handler ทุกตัวรับ interface นี้แทน concrete type ทำให้ test ได้ง่ายด้วย mock

Kubernetes: reconcile loop ใช้ pointer receiver ทุกตัวเพราะ controller มี state ที่ต้องแก้ไข — consistency ของ receiver type สำคัญมาก

method ที่มี value receiver `(c Counter)` แก้ไข `c.count` จะเกิดอะไรกับตัวแปรต้นฉบับ?
method set ของ type ใดที่รวม methods ทั้ง value-receiver และ pointer-receiver?
คุณต้องการนิยาม method บน `type Kelvin float64` มีข้อจำกัดอะไร?
type ของคุณมี 5 methods: 4 ใช้ value receiver และ 1 ใช้ pointer receiver คุณควรทำอย่างไร?