ช่วงบนอินเทอร์เฟซ {} ซึ่งจัดเก็บชิ้นส่วน


99

t interface{}รับสถานการณ์ที่คุณมีฟังก์ชั่นที่ยอมรับ หากพิจารณาแล้วว่าtเป็นชิ้นฉันจะทำrangeอย่างไร

func main() {
    data := []string{"one","two","three"}
    test(data)
    moredata := []int{1,2,3}
    test(data)
}

func test(t interface{}) {
    switch reflect.TypeOf(t).Kind() {
    case reflect.Slice:
        // how do I iterate here?
        for _,value := range t {
            fmt.Println(value)
        }
    }
}

ไปที่ Playground ตัวอย่าง: http://play.golang.org/p/DNldAlNShB


ทำไมไม่มีฟังก์ชันใช้ [] อินเทอร์เฟซ {} แทนล่ะ คุณกำลังพยายามจัดการกับประเภทที่ซับซ้อนหลายประเภทหรือไม่?
Jeremy Wall

2
ใช่สำหรับระบบเทมเพลตดังนั้นอินเทอร์เฟซ {} อาจเป็นแผนที่โครงสร้างสไลซ์หรืออาร์เรย์ ในโค้ดจริงมีคำชี้แจงกรณีอื่น ๆ อีกมากมาย แต่ฉันลบออกจากโพสต์เพื่อให้ปัญหากระชับยิ่งขึ้น
Nucleon

1
Jeremy สตริง [] ไม่ใช่ประเภทย่อยของ [] interface {} ดังนั้นคุณจึงไม่สามารถเรียกใช้ฟังก์ชัน func ([] interface {}) ด้วย [] string หรือ [] int ได้เป็นต้นมันเป็นการดีที่จะ มีคุณลักษณะใน Go to มีประเภทที่มีความหมายว่า "slice of something" ซึ่งคุณสามารถวนซ้ำองค์ประกอบต่างๆเป็นอินเทอร์เฟซ {} ได้ แต่น่าเสียดายที่คุณต้องไตร่ตรองถึงสิ่งนั้น
Bjarke Ebert

@JeremyWall คุณไม่สามารถใช้ [] อินเทอร์เฟซ {} เพื่อทำหน้าที่เป็นสไลซ์ได้ คุณสามารถดูที่นี่
firelyu

คำตอบ:


138

ฉันใช้reflect.ValueOfแล้วถ้ามันเป็นชิ้นคุณสามารถเรียกLen()และIndex()หาค่าเพื่อรับlenชิ้นส่วนและองค์ประกอบที่ดัชนี ฉันไม่คิดว่าคุณจะสามารถใช้ range operation เพื่อทำสิ่งนี้ได้

package main

import "fmt"
import "reflect"

func main() {
    data := []string{"one","two","three"}
    test(data)
    moredata := []int{1,2,3}
    test(moredata)
} 

func test(t interface{}) {
    switch reflect.TypeOf(t).Kind() {
    case reflect.Slice:
        s := reflect.ValueOf(t)

        for i := 0; i < s.Len(); i++ {
            fmt.Println(s.Index(i))
        }
    }
}

ไปที่ Playground ตัวอย่าง: http://play.golang.org/p/gQhCTiwPAq


26
ใช้งานได้ดีขอบคุณ สิ่งเดียวที่ต้องเพิ่มก็คือการs.Index(i)ส่งคืนค่าreflect.Valueนี้ในกรณีของฉันฉันต้องs.Index(i).Interface()อ้างอิงค่าจริง
Nucleon

1
คุณจะทำอย่างไรถ้าคุณมีตัวชี้ไปที่ชิ้นส่วนinterface{}? เช่น moredata := &[]int{1,2,3}
Ryan Walls

1
ตอบคำถามของฉัน: หากคุณมีตัวชี้ไปที่ชิ้นแทนที่จะเป็นชิ้นคุณจะต้องใช้Elem()เพื่อรับค่าพื้นฐาน เช่น reflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
Ryan Walls

จะเป็นอย่างไรถ้าฉันต้องการรับค่าของฟิลด์อินเทอร์เฟซจากส่วนของอินเทอร์เฟซโดยไม่มีโครงสร้างใด ๆ ฉันได้ลองวิธีแก้ปัญหานี้แล้ว แต่ จำกัด ให้รับการอ้างอิงอินเทอร์เฟซจาก slice เท่านั้นไม่ใช่ค่าจริงของฟิลด์
Amandeep kaur

ขอบคุณมากเคล็ดลับนั้นช่วยฉันได้มาก (และปวดหัว!)
Nicolas Garnier

27

คุณไม่จำเป็นต้องใช้การไตร่ตรองหากคุณรู้ว่าควรคาดหวังประเภทใด คุณสามารถใช้สวิตช์ประเภทดังนี้:

package main

import "fmt"

func main() {
    loop([]string{"one", "two", "three"})
    loop([]int{1, 2, 3})
}

func loop(t interface{}) {
    switch t := t.(type) {
    case []string:
        for _, value := range t {
            fmt.Println(value)
        }
    case []int:
        for _, value := range t {
            fmt.Println(value)
        }
    }
}

ตรวจสอบรหัสในสนามเด็กเล่น


สิ่งนี้จะใช้ไม่ได้กับอาร์เรย์ แต่เฉพาะในสไลซ์
user1028741

@ user1028741 ไม่รหัสใช้ได้กับทุกประเภทรวมถึงอาร์เรย์ด้วย + คุณสามารถแบ่งส่วนจากอาร์เรย์array[:]ได้ตลอดเวลา
Inanc Gumus

แต่ประเภทของอาร์เรย์จะเป็นไปตามความจุจริง [3] int ไม่ใช่ประเภทเดียวกับ [2] int หรือ [] int ดังนั้นหากประเภทของ 't' เป็นอาร์เรย์จริงกรณีที่เกี่ยวข้องจะไม่ใช้
user1028741

1
สิ่งที่ฉันหมายถึงคือตัวอย่างของคุณจะไม่ทำงานกับอาร์เรย์ ฉันเปลี่ยนที่นี่: play.golang.org/p/DAsg_0aXz-r
user1028741

คุณไม่สามารถทำให้มันทำงานได้อย่างง่ายดายหากคุณไม่ทราบประเภทของพารามิเตอร์ของคุณตั้งแต่แรก ในการใช้กลอุบายของคุณ (อาร์เรย์ [:]) คุณจะต้องใช้การสะท้อนเพื่อตัดสินใจว่าเป็นอาร์เรย์จากนั้นจึงส่งไปยังอาร์เรย์จากนั้นสร้างส่วนของมัน นั่นเป็นเหตุผลที่ฉันบอกว่ามันทำงานบนชิ้นไม่ใช่ในอาร์เรย์ เห็นได้ชัดว่ามีความพยายามเพียงพอที่คุณจะสามารถสร้างชิ้นส่วนจากอาร์เรย์ได้ ...
user1028741

4

มีข้อยกเว้นอย่างหนึ่งจากการทำงานของอินเทอร์เฟซ {} ที่ @Jeremy Wall ได้ให้ตัวชี้ไว้แล้ว หากข้อมูลที่ส่งผ่านถูกกำหนดให้เป็น [] อินเทอร์เฟซ {} ในขั้นต้น

package main

import (
    "fmt"
)

type interfaceSliceType []interface{}

var interfaceAsSlice interfaceSliceType

func main() {
    loop(append(interfaceAsSlice, 1, 2, 3))
    loop(append(interfaceAsSlice, "1", "2", "3"))
    // or
    loop([]interface{}{[]string{"1"}, []string{"2"}, []string{"3"}})
    fmt.Println("------------------")


    // and of course one such slice can hold any type
    loop(interfaceSliceType{"string", 999, map[int]string{3: "three"}})
}

func loop(slice []interface{}) {
    for _, elem := range slice {
        switch elemTyped := elem.(type) {
        case int:
            fmt.Println("int:", elemTyped)
        case string:
            fmt.Println("string:", elemTyped)
        case []string:
            fmt.Println("[]string:", elemTyped)
        case interface{}:
            fmt.Println("map:", elemTyped)
        }
    }
}

เอาต์พุต:

int: 1
int: 2
int: 3
string: 1
string: 2
string: 3
[]string: [1]
[]string: [2]
[]string: [3]
------------------
string: string
int: 999
map: map[3:three]

ลองดู

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