alcance sobre a interface {} que armazena uma fatia


96

Dado o cenário em que você tem uma função que aceita t interface{}. Se for determinado que té uma fatia, como faço para rangesobre essa fatia?

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

Exemplo de Go Playground: http://play.golang.org/p/DNldAlNShB


Por que não fazer com que a função tenha uma [] interface {} em vez disso? Você está tentando lidar com vários tipos complexos?
— Jeremy Wall

2
Sim, é para um sistema de modelos, então a interface {} pode ser um mapa, estrutura, fatia ou matriz. No código real, há muito mais instruções de caso, mas removi-as da postagem para tornar o problema mais conciso.
— Nucleon

1
Jeremy, uma [] string não é um subtipo de [] interface {}, então você não pode chamar uma função func ([] interface {}) com uma [] string ou [] int, etc. Seria bom tem um recurso em Go para ter um tipo que significa "fatia de algo", onde você pode iterar sobre os elementos como interface {}, mas infelizmente você precisa de reflexão para isso.
— Bjarke Ebert

@JeremyWall Você não pode usar a [] interface {} para atuar como fatia. Você pode consultar aqui .
— firelyu de

Respostas:


137

Bem, eu usei reflect.ValueOfe então se for uma fatia você pode chamar Len()e Index()no valor para obter o lenda fatia e do elemento em um índice. Não acho que você será capaz de usar a operação de alcance para fazer isso.

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

Exemplo de Go Playground: http://play.golang.org/p/gQhCTiwPAq


23
Funciona muito bem, obrigado. A única coisa a adicionar seria que s.Index(i)retorna um, reflect.Valueportanto, no meu caso, eu precisava s.Index(i).Interface()fazer referência ao valor real.
— Nucleon

1
O que você faria se tivesse um ponteiro para uma fatia interface{}? por exemplo moredata := &[]int{1,2,3}
— Ryan Walls

1
Respondendo à minha pergunta: se você tiver um ponteiro para uma fatia em vez de uma fatia, precisará usar Elem()para obter o valor subjacente. por exemplo reflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
— Ryan Walls

e se eu quiser obter o valor do campo de interface da fatia da interface sem qualquer estrutura. Eu tentei essa solução, mas ela é limitada para obter referência de interface apenas da fatia e não dos valores reais dos campos.
— Amandeep Kaur

woh muito obrigado, esse truque me economizou muito tempo (e dor de cabeça!)
— Nicolas Garnier

26

Você não precisa usar reflexão se souber quais tipos esperar. Você pode usar uma chave de tipo , como esta:

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

Verifique o código no playground .


Porém, isso não funcionará em uma matriz, apenas em uma fatia
— user1028741

@ user1028741 Não, o código funciona para todos os tipos, incluindo arrays + Você sempre pode pegar uma fatia de um array array[:].
— Inanc Gumus

Mas o tipo de array será com a capacidade real dele. [3] int não é do mesmo tipo que [2] int ou [] int. Portanto, se o tipo de 't' for realmente uma matriz, o caso relevante não se aplicará.
— user1028741

O que quero dizer é que seu exemplo não funcionará em matrizes. Eu mudei aqui: play.golang.org/p/DAsg_0aXz-r
— user1028741

Você não pode fazer isso funcionar facilmente, se você não souber o tipo de seu parâmetro em primeiro lugar. Para usar seu truque (array [:]), você terá que usar a reflexão para decidir que é um array, então lançar em array - e então gerar uma fatia dele. É por isso que eu disse que funciona em uma fatia, não em uma matriz. Obviamente, com esforço suficiente, você pode produzir uma fatia da matriz ...
— user1028741,

4

há uma exceção na maneira como a interface {} se comporta, @Jeremy Wall já deu um ponteiro. se os dados transmitidos forem definidos como [] interface {} inicialmente.

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

resultado:

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]

Experimente

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.