intervallo sull'interfaccia {} che memorizza una fetta


96

Dato lo scenario in cui hai una funzione che accetta t interface{}. Se è determinato che tè una fetta, come faccio a rangesuperare quella fetta?

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

Vai ad esempio di Playground: http://play.golang.org/p/DNldAlNShB


Perché invece la funzione non prende un'interfaccia [] {}? Stai cercando di gestire più tipi complessi?
Jeremy Wall

2
Sì, è per un sistema di modelli, quindi l'interfaccia {} potrebbe essere una mappa, uno struct, uno slice o un array. Nel codice reale ci sono molte più istruzioni case, ma le ho rimosse dal post per rendere il problema più conciso.
Nucleon

1
Jeremy, una stringa [] non è un sottotipo di [] interfaccia {}, quindi non puoi chiamare una funzione func ([] interfaccia {}) con una [] stringa o [] int, ecc. Sarebbe bello hanno una funzionalità in Go per avere un tipo che significa "fetta di qualcosa", dove puoi quindi iterare sugli elementi come interfaccia {}, ma sfortunatamente hai bisogno di una riflessione per questo.
Bjarke Ebert

@JeremyWall Non puoi utilizzare [] l'interfaccia {} per agire come slice. Puoi fare riferimento qui .
firelyu

Risposte:


137

Bene ho usato reflect.ValueOfe quindi se è uno slice puoi chiamare Len()e Index()sul valore per ottenere lenlo slice e l'elemento in un indice. Non credo che sarai in grado di utilizzare il funzionamento a distanza per farlo.

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

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


23
Funziona alla grande, grazie. L'unica cosa da aggiungere è che s.Index(i)restituisce un, reflect.Valuequindi nel mio caso avevo bisogno di s.Index(i).Interface()fare riferimento al valore effettivo.
Nucleon

1
Cosa faresti se avessi un puntatore a uno slice interface{}? ad esempio moredata := &[]int{1,2,3}
Ryan Walls,

1
Rispondendo alla mia domanda: se hai un puntatore a una fetta invece di una fetta, dovrai usarla Elem()per ottenere il valore sottostante. ad esempio reflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
Ryan Walls,

cosa succede se voglio ottenere il valore del campo di interfaccia dalla fetta di interfaccia senza alcuna struttura. Ho provato questa soluzione ma si limita a ottenere il riferimento all'interfaccia solo dalla sezione, non i valori effettivi dei campi.
Amandeep kaur,

woh molte grazie, quel trucco mi ha fatto risparmiare un sacco di tempo (e mal di testa!)
Nicolas Garnier

26

Non è necessario utilizzare la riflessione se sai quali tipi aspettarti. Puoi usare un interruttore di tipo , come questo:

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

Controlla il codice nel parco giochi .


Tuttavia, questo non funzionerà su un array, ma solo su uno slice
user1028741

@ user1028741 No, il codice funziona per ogni tipo, inclusi gli array + Puoi sempre prendere uno slice da un array array[:].
Inanc Gumus,

Ma il tipo di un array dipenderà dalla capacità effettiva di esso. [3] int non è dello stesso tipo di [2] int o [] int. Pertanto, se il tipo di "t" è effettivamente un array, il caso pertinente non verrà applicato.
user1028741

Quello che voglio dire è che il tuo esempio non funzionerà sugli array. L'ho cambiato qui: play.golang.org/p/DAsg_0aXz-r
user1028741

Non puoi farlo funzionare facilmente, se non conosci il tipo di parametro in primo luogo. Per poter usare il tuo trucco (array [:]), dovrai usare la reflection per decidere che si tratta di un array, quindi eseguire il cast su array e quindi generare una parte di esso. Ecco perché ho detto che funziona su uno slice, non su un array. Chiaramente con abbastanza sforzo puoi produrre una fetta dall'array ...
user1028741

4

c'è un'eccezione dal modo in cui si comporta l'interfaccia {}, @Jeremy Wall ha già fornito un puntatore. se i dati passati sono inizialmente definiti come [] interfaccia {}.

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

produzione:

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]

Provalo

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.