ไม่สามารถแปลงข้อมูล (ชนิดอินเตอร์เฟส {}) เป็นประเภทสตริง: ต้องการการยืนยันประเภท


178

ฉันค่อนข้างใหม่ที่จะไปและฉันกำลังเล่นกับแพคเกจการแจ้งเตือนนี้

ตอนแรกฉันมีรหัสที่มีลักษณะเช่นนี้:

func doit(w http.ResponseWriter, r *http.Request) {
    notify.Post("my_event", "Hello World!")
    fmt.Fprint(w, "+OK")
}

ฉันต้องการต่อท้ายบรรทัดใหม่Hello World!แต่ไม่ใช่ในฟังก์ชั่นdoitด้านบนเพราะนั่นจะเป็นเรื่องเล็กน้อย แต่handlerหลังจากนั้นเช่นนี้ด้านล่าง:

func handler(w http.ResponseWriter, r *http.Request) {
    myEventChan := make(chan interface{})
    notify.Start("my_event", myEventChan)
    data := <-myEventChan
    fmt.Fprint(w, data + "\n")
}

หลังgo run:

$ go run lp.go 
# command-line-arguments
./lp.go:15: invalid operation: data + "\n" (mismatched types interface {} and string)

หลังจากเล็กน้อยของ Googling ผมพบว่าคำถามนี้ในดังนั้น

จากนั้นฉันอัปเดตรหัสของฉันเป็น:

func handler(w http.ResponseWriter, r *http.Request) {
    myEventChan := make(chan interface{})
    notify.Start("my_event", myEventChan)
    data := <-myEventChan
    s:= data.(string) + "\n"
    fmt.Fprint(w, s)
}

นี่คือสิ่งที่ฉันควรจะทำอย่างไร ข้อผิดพลาดของคอมไพเลอร์ของฉันหายไปดังนั้นฉันคิดว่ามันค่อนข้างดี? มันมีประสิทธิภาพไหม? คุณควรทำมันแตกต่างกันอย่างไร

คำตอบ:


292

ตามข้อกำหนด Go :

สำหรับนิพจน์ x ของประเภทอินเตอร์เฟสและประเภท T นิพจน์หลัก x (T) ยืนยันว่า x ไม่ใช่ศูนย์และค่าที่เก็บไว้ใน x เป็นประเภท T

"การยืนยันประเภท" ช่วยให้คุณสามารถประกาศค่าอินเทอร์เฟซที่มีประเภทคอนกรีตบางชนิดหรือว่าประเภทคอนกรีตเป็นไปตามอินเตอร์เฟซอื่น

ในตัวอย่างของคุณคุณกำลังยืนยันข้อมูล (ชนิดอินเตอร์เฟส {}) มีสตริงชนิดที่เป็นรูปธรรม หากคุณผิดโปรแกรมจะตื่นตระหนกขณะรันไทม์ คุณไม่จำเป็นต้องกังวลเกี่ยวกับประสิทธิภาพการตรวจสอบเพียงต้องการเปรียบเทียบค่าตัวชี้สองค่า

หากคุณไม่แน่ใจว่ามันเป็นสตริงหรือไม่คุณสามารถทดสอบโดยใช้ไวยากรณ์ส่งคืนสองรายการ

str, ok := data.(string)

หากข้อมูลไม่ใช่สตริง ok จะเป็นเท็จ มันเป็นเรื่องธรรมดาที่จะห่อคำสั่งดังกล่าวลงในคำสั่ง if เช่นนั้น:

if str, ok := data.(string); ok {
    /* act on str */
} else {
    /* not string */
}

29

ยืนยันประเภท

เรื่องนี้เป็นที่รู้จักtype assertionใน golang และมันเป็นเรื่องธรรมดา

นี่คือคำอธิบายจากทัวร์ของไป :

การยืนยันประเภทให้การเข้าถึงค่าที่เป็นรูปธรรมของค่าอินเทอร์เฟซ

t := i.(T)

คำสั่งนี้อ้างว่าค่าของอินเตอร์เฟสที่ฉันถือเป็นรูปธรรม T และกำหนดค่า T พื้นฐานให้กับตัวแปร t

หากฉันไม่ได้ถือ T คำสั่งจะเรียกความหวาดกลัว

เมื่อต้องการทดสอบว่าค่าของอินเทอร์เฟซมีชนิดเฉพาะการยืนยันชนิดสามารถส่งคืนสองค่า: ค่าที่อยู่ภายใต้และค่าบูลีนที่รายงานว่าการยืนยันสำเร็จหรือไม่

t, ok := i.(T)

ถ้าฉันถือ T แล้ว t จะเป็นค่าพื้นฐานและตกลงจะเป็นจริง

หากไม่เป็นเช่นนั้น ok จะเป็นเท็จและ t จะเป็นค่าศูนย์ของประเภท T และจะไม่มีการตื่นตระหนก

หมายเหตุ: มูลค่าiที่ควรจะเป็นประเภทอินเตอร์เฟซ

ผิดพลาด

แม้ว่าiจะเป็นประเภท[]iอินเตอร์เฟสไม่ใช่ประเภทอินเตอร์เฟส ดังนั้นเพื่อที่จะแปลง[]iเป็นประเภทของค่าเราต้องทำทีละอย่าง:

// var items []i
for _, item := range items {
    value, ok := item.(T)
    dosomethingWith(value)
}

ประสิทธิภาพ

สำหรับประสิทธิภาพอาจช้ากว่าการเข้าถึงค่าจริงตามที่แสดงในคำตอบสแต็คโอเวอร์โฟลว์นี้


13
//an easy way:
str := fmt.Sprint(data)

21
เพิ่มคำอธิบายพร้อมคำตอบสำหรับวิธีที่คำตอบนี้ช่วย OP ในการแก้ไขปัญหาปัจจุบัน
Kяσѕρєя K

3

ในฐานะที่ถามโดย @ ρяσѕρєяคำอธิบายสามารถพบได้ที่https://golang.org/pkg/fmt/#Sprint คำอธิบายที่เกี่ยวข้องสามารถพบได้ที่https://stackoverflow.com/a/44027953/12817546และhttps://stackoverflow.com/a/42302709/12817546 นี่คือคำตอบของ @ Yuanbo เต็ม

package main

import "fmt"

func main() {
    var data interface{} = 2
    str := fmt.Sprint(data)
    fmt.Println(str)
}

1
ฉันเดาว่าคุณสามารถรวมคำตอบทั้งสองได้โดยเพียงแค่แก้ไข @ Yuanbo's - คุณทั้งคู่จะได้รับเครดิตและเพิ่มคะแนน 'ประโยชน์' ของคุณตามลำดับ
Gwyneth Llewelyn
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.