Go reflect.TypeAssert: ตรวจชนิดจาก reflect.Value โดยไม่สร้าง interface กลาง
โค้ดที่ใช้ reflection มักจบด้วย value.Interface().(T) เพื่อแปลงค่ากลับเป็นชนิดที่รู้จักครับ วิธีนี้อ่านได้ แต่สร้างค่า interface{} คั่นกลางโดยไม่จำเป็น reflect.TypeAssert รับ reflect.Value โดยตรงและคืน (T, bool) จึงสื่อเจตนาว่า “ลองตรวจชนิดนี้” ชัดกว่า
ให้นึกถึงด่านคัดพัสดุ วิธีเดิมแกะของออกจากกล่องกลางก่อนเทียบแม่แบบ ส่วนวิธีใหม่ยกกล่องเดิมเข้าเครื่องวัดแล้วตอบทันทีว่ารูปทรงตรงหรือไม่ เราไม่ได้เลิกใช้ reflection แต่ลดขั้นตอนระหว่างทางในจุดที่รู้ชนิดปลายทางอยู่แล้ว
package main
import (
"fmt"
"reflect"
)
type Job struct {
Name string
}
func decodeJob(value reflect.Value) (Job, bool) {
return reflect.TypeAssert[Job](value)
}
func main() {
job, ok := decodeJob(reflect.ValueOf(Job{Name: "daily-report"}))
if !ok || job.Name != "daily-report" {
panic("type assertion failed")
}
if _, ok := decodeJob(reflect.ValueOf("wrong type")); ok {
panic("unexpected type match")
}
fmt.Println("reflect.TypeAssert checks passed")
}
จุดที่เครื่องมือช่วยย้ายได้
ตัววิเคราะห์ modernize ของ golang.org/x/tools เสนอการเปลี่ยนแบบสองค่า เช่น v.Interface().(string) เป็น reflect.TypeAssert[string](v) เพราะทั้งคู่รายงานความสำเร็จผ่าน boolean ได้ แต่ไม่แก้ assertion แบบค่าเดียวที่ตั้งใจ panic เมื่อชนิดผิด เนื่องจาก TypeAssert ไม่ได้มีรูปแบบ panic เดียวกัน
อย่าใช้ reflection เพียงเพราะ API ใหม่ดูเร็วขึ้น ถ้า caller ส่ง generic type parameter หรือ interface ปกติได้ตั้งแต่ต้น โค้ดแบบ static ยังอ่านง่ายและให้ compiler ช่วยมากกว่า จุดที่เหมาะคือ serializer, dependency injection หรือ adapter ที่รับ reflect.Value อยู่แล้ว และควร benchmark เส้นทางจริงก่อนอ้างว่าลด allocation ได้อย่างมีนัยสำคัญ
Recap
- ใช้
TypeAssert[T]เมื่อมีreflect.Valueและต้องการผลแบบ(T, bool) - อย่าเปลี่ยน assertion ที่ตั้งใจ panic โดยไม่ทบทวน behavior
- รัน
modernizeเป็นคำแนะนำ ไม่ใช่แก้อัตโนมัติโดยไม่ review - ถอด reflection ออกได้เมื่อไร ให้เลือก type-safe API ก่อน