zakres ponad interfejsem {}, który przechowuje wycinek

99

Biorąc pod uwagę scenariusz, w którym masz funkcję, która akceptuje t interface{}. Jeśli okaże się, że tjest to kawałek, jak mam rangego nadrobić?

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

Przykład Go Playground: http://play.golang.org/p/DNldAlNShB

Nukleon
źródło
Dlaczego zamiast tego funkcja miałaby przyjąć interfejs [] {}? Czy próbujesz obsłużyć wiele złożonych typów?
Jeremy Wall,
2
Tak, to jest dla systemu szablonów, więc interfejs {} może być mapą, strukturą, wycinkiem lub tablicą. W prawdziwym kodzie jest o wiele więcej opisów przypadków, ale usunąłem je z postu, aby problem był bardziej zwięzły.
Nucleon
1
Jeremy, łańcuch [] nie jest podtypem [] interface {}, więc nie możesz wywołać funkcji func ([] interface {}) z ciągiem [] string lub [] int, itd. Byłoby miło mają w Go funkcję, która ma typ oznaczający „kawałek czegoś”, gdzie możesz następnie iterować po elementach jako interfejs {}, ale niestety potrzebujesz do tego refleksji.
Bjarke Ebert
@JeremyWall Nie możesz używać [] interfejsu {} do działania jako wycinek. Możesz odnieść się tutaj .
firelyu

Odpowiedzi:

138

Cóż, użyłem, reflect.ValueOfa następnie, jeśli jest to wycinek, możesz wywołać Len()i Index()wartość, aby uzyskać lenwycinek i element w indeksie. Nie sądzę, abyś mógł użyć zakresu działania, aby to zrobić.

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

Przykład Go Playground: http://play.golang.org/p/gQhCTiwPAq

masebase
źródło
26
Działa świetnie, dziękuję. Jedyną rzeczą do dodania byłoby s.Index(i)zwrócenie wartości, reflect.Valuewięc w moim przypadku musiałem s.Index(i).Interface()odwołać się do rzeczywistej wartości.
Nucleon,
1
Co byś zrobił, gdybyś miał wskaźnik do plastra interface{}? np. moredata := &[]int{1,2,3}
Ryan Walls
1
Odpowiadając na moje pytanie: jeśli zamiast wycinka masz wskaźnik do wycinka, musisz użyć, Elem()aby uzyskać podstawową wartość. np. reflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
Ryan Walls
co jeśli chcę uzyskać wartość pola interfejsu z wycinka interfejsu bez żadnej struktury. Wypróbowałem to rozwiązanie, ale jest ograniczone do uzyskania odniesienia do interfejsu tylko z wycinka, a nie z rzeczywistych wartości pól.
Amandeep kaur
och, wielkie dzięki, ta sztuczka zaoszczędziła mi dużo czasu (i bólu głowy!)
Nicolas Garnier
26

Nie musisz używać refleksji, jeśli wiesz, jakich typów się spodziewać. Możesz użyć przełącznika typu , na przykład:

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

Sprawdź kod na placu zabaw .

Inanc Gumus
źródło
To jednak nie zadziała w przypadku tablicy, tylko w przypadku wycinka
user1028741
@ user1028741 Nie, kod działa dla każdego typu łącznie z tablicami + Zawsze możesz pobrać wycinek z tablicy array[:].
Inanc Gumus
Ale typ tablicy będzie odpowiadał jej rzeczywistej pojemności. [3] int nie jest tego samego typu co [2] int lub [] int. Dlatego jeśli typ „t” jest faktycznie tablicą, odpowiednia wielkość liter nie będzie miała zastosowania.
user1028741
1
Mam na myśli to, że twój przykład nie zadziała na tablicach. Zmieniłem to tutaj: play.golang.org/p/DAsg_0aXz-r
user1028741
Nie możesz łatwo sprawić, żeby to zadziałało, jeśli nie znasz typu swojego parametru. Aby użyć swojej sztuczki (tablica [:]), musisz użyć odbicia, aby zdecydować, że jest to tablica, a następnie rzucić na tablicę - a następnie wygenerować jej wycinek. Dlatego powiedziałem, że działa na wycinku, a nie na tablicy. Najwyraźniej przy odpowiednim wysiłku możesz stworzyć wycinek z tablicy ...
user1028741
4

jest jeden wyjątek od sposobu, w jaki zachowuje się {} interfejs, @Jeremy Wall dał już wskaźnik. jeśli przekazywane dane są początkowo zdefiniowane jako [] interfejs {}.

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

wynik:

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]

Wypróbuj to

Matus Kral
źródło