rentang di atas antarmuka {} yang menyimpan sepotong

99

Mengingat skenario di mana Anda memiliki fungsi yang menerima t interface{}. Jika ditentukan bahwa tadalah irisan, bagaimana cara rangemengatasinya?

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)
        }
    }
}

Contoh Go Playground: http://play.golang.org/p/DNldAlNShB

Nukleon
sumber
Mengapa fungsi tidak mengambil antarmuka [] {} sebagai gantinya? Apakah Anda mencoba menangani beberapa tipe kompleks?
Jeremy Wall
2
Ya, ini untuk sistem templat sehingga antarmuka {} bisa berupa peta, struct, slice, atau larik. Dalam kode sebenarnya ada lebih banyak pernyataan kasus, tetapi saya menghapusnya dari posting untuk membuat masalah lebih ringkas.
Nucleon
1
Jeremy, sebuah string [] bukanlah subtipe dari [] antarmuka {}, jadi Anda tidak bisa memanggil fungsi func ([] antarmuka {}) dengan string [] atau [] int, dll. Akan menyenangkan untuk memiliki fitur di Go untuk memiliki tipe yang berarti "potongan sesuatu", di mana Anda kemudian dapat mengulang elemen sebagai antarmuka {}, tapi sayangnya Anda membutuhkan refleksi untuk itu.
Bjarke Ebert
@JeremyWall Anda tidak dapat menggunakan [] antarmuka {} untuk bertindak sebagai slice. Anda bisa merujuk di sini .
firelyu

Jawaban:

138

Saya menggunakan reflect.ValueOfdan kemudian jika itu adalah irisan yang dapat Anda panggil Len()dan Index()nilai untuk mendapatkan lenpotongan dan elemen pada indeks. Saya tidak berpikir Anda akan dapat menggunakan jangkauan operasi untuk melakukan ini.

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))
        }
    }
}

Contoh Go Playground: http://play.golang.org/p/gQhCTiwPAq

masebase
sumber
26
Bekerja dengan baik, terima kasih. Satu-satunya hal yang harus ditambahkan adalah yang s.Index(i)mengembalikan reflect.Valuejadi dalam kasus saya, saya perlu s.Index(i).Interface()mereferensikan nilai sebenarnya.
Nucleon
1
Apa yang akan Anda lakukan jika Anda memiliki penunjuk ke sebuah slice interface{}? misalnya moredata := &[]int{1,2,3}
Ryan Walls
1
Menjawab pertanyaan saya: Jika Anda memiliki penunjuk ke sepotong alih-alih sepotong, Anda harus menggunakan Elem()untuk mendapatkan nilai yang mendasarinya. misalnya reflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
Ryan Walls
bagaimana jika saya ingin mendapatkan nilai bidang antarmuka dari potongan antarmuka tanpa struct apa pun. Saya telah mencoba solusi ini tetapi terbatas untuk mendapatkan referensi antarmuka hanya dari irisan bukan nilai aktual bidang.
Amandeep kaur
woh banyak terima kasih, trik itu menyelamatkan saya banyak waktu (dan sakit kepala!)
Nicolas Garnier
26

Anda tidak perlu menggunakan refleksi jika Anda tahu tipe mana yang diharapkan. Anda dapat menggunakan sakelar tipe , seperti ini:

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)
        }
    }
}

Lihat kode di taman bermain .

Inanc Gumus
sumber
Ini tidak akan bekerja pada sebuah array, hanya pada sepotong
user1028741
@ user1028741 Tidak, kode bekerja untuk setiap tipe termasuk array + Anda selalu dapat mengambil potongan dari array array[:].
Inanc Gumus
Tetapi jenis array akan sesuai dengan kapasitas sebenarnya. [3] int bukan dari tipe yang sama dengan [2] int atau [] int. Oleh karena itu, jika tipe 't' sebenarnya adalah array kasus yang relevan tidak akan berlaku.
pengguna1028741
1
Yang saya maksud adalah contoh Anda tidak akan berfungsi pada array. Saya mengubahnya di sini: play.golang.org/p/DAsg_0aXz-r
user1028741
Anda tidak dapat dengan mudah membuatnya berfungsi, jika Anda tidak mengetahui jenis parameter Anda sejak awal. Untuk menggunakan trik Anda (array [:]), Anda harus menggunakan refleksi untuk memutuskan bahwa itu adalah sebuah array, lalu cast ke array - kemudian buat potongan darinya. Itulah mengapa saya mengatakan ini berfungsi pada slice, bukan pada array. Jelas dengan usaha yang cukup Anda dapat menghasilkan potongan dari array ...
user1028741
4

ada satu pengecualian dari cara antarmuka {} berperilaku, @Jeremy Wall sudah memberikan pointer. jika data yang diteruskan awalnya didefinisikan sebagai [] antarmuka {}.

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)
        }
    }
}

keluaran:

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]

mencobanya

Matus Kral
sumber