พิมพ์การแปลงส่วนของอินเทอร์เฟซ


194

ฉันอยากรู้ว่าทำไมไปโดยปริยายกวนแปลง[]Tไป[]interface{}เมื่อมันโดยปริยายจะแปลงไปT interface{}มีบางอย่างที่ไม่สำคัญกับการแปลงนี้ที่ฉันขาดไปหรือไม่?

ตัวอย่าง:

func foo([]interface{}) { /* do something */ }

func main() {
    var a []string = []string{"hello", "world"}
    foo(a)
}

go build บ่น

ไม่สามารถใช้สตริง (ประเภท []) เป็นประเภท [] อินเตอร์เฟส {} ในอาร์กิวเมนต์ของฟังก์ชัน

และถ้าฉันพยายามทำมันอย่างชัดเจนสิ่งเดียวกัน: b := []interface{}(a)บ่น

ไม่สามารถแปลงสตริง (พิมพ์ []) เป็นพิมพ์ [] อินเตอร์เฟส {}

ดังนั้นทุกครั้งที่ฉันจำเป็นต้องทำการแปลงนี้ (ซึ่งดูเหมือนจะเกิดขึ้นมาก) ฉันได้ทำสิ่งนี้:

b = make([]interface{}, len(a), len(a))
for i := range a {
    b[i] = a[i]
}

มีวิธีที่ดีกว่าในการทำเช่นนี้หรือฟังก์ชันไลบรารีมาตรฐานเพื่อช่วยในการแปลงเหล่านี้หรือไม่ ดูเหมือนโง่ที่จะเขียนโค้ดพิเศษ 4 บรรทัดทุกครั้งที่ฉันต้องการเรียกใช้ฟังก์ชันที่สามารถรับรายการเช่น int หรือสตริง


ดังนั้นคุณจึงกำหนด "แปลง [] T เป็น [] ส่วนติดต่อ {}" โดยปริยายเพื่อหมายถึง "การจัดสรรส่วนใหม่และคัดลอกองค์ประกอบทั้งหมดไป" นั่นไม่สอดคล้องกับสิ่งที่ "แปลง T ไปยังอินเตอร์เฟส {}" โดยปริยายซึ่งเป็นเพียงมุมมองของค่าเดียวกันกับประเภทสแตติกทั่วไปมากกว่า ไม่มีการคัดลอก; และถ้าคุณพิมพ์ - ยืนยันกลับไปพิมพ์ T คุณจะยังคงได้รับสิ่งเดียวกัน
newacct

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

คำตอบ:


215

ใน Go มีกฎทั่วไปที่ไวยากรณ์ไม่ควรซ่อนการดำเนินการที่ซับซ้อน / มีราคาแพง แปลงstringไปสู่การinterface{}ที่จะทำใน O (1) เวลา แปลง[]stringไปยังinterface{}จะทำยังใน O (1) เวลาตั้งแต่ชิ้นยังคงเป็นหนึ่งค่า อย่างไรก็ตามการแปลง[]stringไปสู่การ[]interface{}เป็น O (n) interface{}เวลาเพราะองค์ประกอบของชิ้นแต่ละคนจะต้องแปลงเป็น

ข้อยกเว้นเดียวสำหรับกฎนี้คือการแปลงสตริง เมื่อแปลง a stringถึงและจาก a []byteหรือ a []runeGo จะทำงาน O (n) แม้ว่า Conversion จะเป็น "ไวยากรณ์"

ไม่มีฟังก์ชันไลบรารีมาตรฐานที่จะทำการแปลงนี้ให้คุณ คุณสามารถทำการสะท้อนได้ แต่มันจะช้ากว่าตัวเลือกสามบรรทัด

ตัวอย่างที่มีการสะท้อนกลับ:

func InterfaceSlice(slice interface{}) []interface{} {
    s := reflect.ValueOf(slice)
    if s.Kind() != reflect.Slice {
        panic("InterfaceSlice() given a non-slice type")
    }

    ret := make([]interface{}, s.Len())

    for i:=0; i<s.Len(); i++ {
        ret[i] = s.Index(i).Interface()
    }

    return ret
}

ตัวเลือกที่ดีที่สุดของคุณคือใช้รหัสบรรทัดที่คุณให้ไว้ในคำถามของคุณ:

b := make([]interface{}, len(a))
for i := range a {
    b[i] = a[i]
}

3
มันอาจจะช้ากว่า แต่มันจะใช้งานได้กับชิ้นส่วนใด ๆ โดยทั่วไป
newacct

คำตอบทั้งหมดนี้ใช้ได้กับmaps เกินไป btw
RickyA

นอกจากนี้ยังนำไปใช้channelsเช่นกัน
Justin Ohms

ขอบคุณสำหรับคำอธิบายที่ชัดเจนถ้าเป็นไปได้คุณสามารถเพิ่มการอ้างอิง เพื่อConverting a []string to an interface{} is also done in O(1) time?
Mayur

56

สิ่งที่คุณจะหายไปก็คือว่าTและinterface{}ซึ่งถือเป็นค่าTมีการแสดงที่แตกต่างกันในหน่วยความจำเพื่อให้ไม่สามารถแปลงนิด

ตัวแปรประเภทTเป็นเพียงค่าในหน่วยความจำ ไม่มีข้อมูลประเภทที่เกี่ยวข้อง (ใน Go ตัวแปรทุกตัวมีชนิดเดียวที่รู้จักในเวลารวบรวมไม่ใช่เวลาทำงาน) มันถูกแสดงในหน่วยความจำเช่นนี้:

  • ความคุ้มค่า

การinterface{}เก็บตัวแปรประเภทหนึ่งTไว้ในหน่วยความจำเช่นนี้

  • ตัวชี้เพื่อพิมพ์ T
  • ความคุ้มค่า

ดังนั้นกลับไปที่คำถามเดิมของคุณมาทำไมกวนไปโดยปริยายแปลง[]Tไป[]interface{}?

การแปลง[]Tเป็น[]interface{}ส่วนเกี่ยวข้องกับการสร้างค่าใหม่interface {}ซึ่งเป็นการดำเนินการที่ไม่น่าสนใจเนื่องจากโครงร่างในหน่วยความจำแตกต่างอย่างสิ้นเชิง


10
นี่เป็นข้อมูลและเขียนอย่างดี (+1) แต่คุณไม่ได้พูดถึงส่วนอื่น ๆ ของคำถาม: "มีวิธีที่ดีกว่าในการทำเช่นนี้ ... " (-1)
weberc2

13

นี่คือคำอธิบายอย่างเป็นทางการ: https://github.com/golang/go/wiki/InterfaceSlice

var dataSlice []int = foo()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
    interfaceSlice[i] = d
}

ข้อมูลดี แต่ไม่ใช่คำอธิบายอย่างเป็นทางการ มันเป็นวิกิที่ทุกคนสามารถแก้ไขได้
Inanc Gumus

ฉันจะแปลง[]interfaceเป็นประเภทที่ฉันคาดหวังได้[]map[string]interface{}อย่างไร
Lewis Chan

8

ลองinterface{}แทน หากต้องการเหวี่ยงเป็นชิ้นให้ลอง

func foo(bar interface{}) {
    s := bar.([]string)
    // ...
}

3
สิ่งนี้ทำให้เกิดคำถามต่อไป: วิธีที่ OP ควรจะทำซ้ำbarเพื่อแปลว่า "ชิ้นส่วนใด ๆ "? โปรดสังเกตว่าสามสายการบินของเขาสร้างแบบคอนกรีตที่[]interface{}ไม่ใช่[]stringหรือฝานชนิดอื่น
kostix

14
-1: ใช้งานได้เฉพาะในกรณีที่แถบเป็นสตริง [] ซึ่งในกรณีนี้คุณอาจเขียนได้เช่นกัน:func foo(bar []string) { /* ... */ }
weberc2

2
ใช้สวิตช์ชนิดหรือใช้การสะท้อนในคำตอบที่ยอมรับ
dskinner

OP จะต้องคิดออกประเภทของเขาในเวลาทำงาน แต่อย่างใด การใช้ที่ไม่ใช่ชิ้นจะทำให้คอมไพเลอร์ไม่บ่นเมื่อผ่านการโต้แย้งในขณะที่interface{} foo(a)เขาสามารถใช้การสะท้อนหรือพิมพ์สลับ ( golang.org/doc/effective_go.html#type_switch ) ภายในfooก็ได้
Cassiohg

2

แปลงinterface{}เป็นรูปแบบใดก็ได้

ไวยากรณ์:

result := interface.(datatype)

ตัวอย่าง:

var employee interface{} = []string{"Jhon", "Arya"}
result := employee.([]string)   //result type is []string.

2
คุณแน่ใจหรือไม่ ฉันไม่คิดว่ามันถูกต้อง คุณจะเห็นมัน: invalid type assertion: potato.([]string) (non-interface type []interface {} on left)
Adriano Tadao

2

ในกรณีที่คุณต้องการรหัสย่อเพิ่มเติมคุณสามารถสร้างประเภทใหม่สำหรับผู้ช่วยได้

type Strings []string

func (ss Strings) ToInterfaceSlice() []interface{} {
    iface := make([]interface{}, len(ss))
    for i := range ss {
        iface[i] = ss[i]
    }
    return iface
}

แล้วก็

a := []strings{"a", "b", "c", "d"}
sliceIFace := Strings(a).ToInterfaceSlice()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.