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


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


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 .


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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.