диапазон по интерфейсу {}, в котором хранится фрагмент

99

Учитывая сценарий, в котором у вас есть функция, которая принимает t interface{}. Если определено, что tэто срез, как мне пройти rangeнад этим срезом?

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

Пример игровой площадки Go: http://play.golang.org/p/DNldAlNShB

Нуклон
источник
Почему бы вместо этого не использовать [] интерфейс {}? Вы пытаетесь обрабатывать несколько сложных типов?
Джереми Уолл
2
Да, это для системы шаблонов, поэтому interface {} может быть картой, структурой, фрагментом или массивом. В реальном коде гораздо больше операторов case, но я удалил их из сообщения, чтобы сделать проблему более лаконичной.
Nucleon
1
Джереми, строка [] не является подтипом [] interface {}, поэтому вы не можете вызывать функцию func ([] interface {}) с помощью строки [] или [] int и т. Д. Было бы неплохо, чтобы в Go есть возможность иметь тип, означающий «кусок чего-то», где вы затем можете перебирать элементы как interface {}, но, к сожалению, для этого вам понадобится отражение.
Bjarke Ebert
@JeremyWall Вы не можете использовать [] interface {} в качестве фрагмента. Вы можете обратиться сюда .
firelyu

Ответы:

138

Хорошо, я использовал, reflect.ValueOfа затем, если это срез, вы можете вызвать Len()и Index()по значению, чтобы получить lenсрез и элемент по индексу. Я не думаю, что вы сможете использовать для этого управление диапазоном.

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: http://play.golang.org/p/gQhCTiwPAq

Masebase
источник
26
Отлично работает, спасибо. Единственное, что нужно добавить, это то, что s.Index(i)возвращает a, reflect.Valueпоэтому в моем случае мне нужно s.Index(i).Interface()было ссылаться на фактическое значение.
Nucleon
1
Что бы вы сделали, если бы у вас был указатель на срез interface{}? например moredata := &[]int{1,2,3}
Райан Уоллс
1
Отвечая на мой вопрос: если у вас есть указатель на срез вместо среза, вам нужно будет использовать его Elem()для получения базового значения. например reflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
Райан Уоллс
что, если я хочу получить значение поля интерфейса из фрагмента интерфейса без какой-либо структуры. Я пробовал это решение, но оно ограничено получением ссылки на интерфейс только из среза, а не фактических значений полей.
Амандип Каур
О, большое спасибо, этот трюк сэкономил мне много времени (и головной боли!)
Николас Гарнье,
27

Вам не нужно использовать отражение, если вы знаете, каких типов ожидать. Вы можете использовать переключатель типа , например:

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

Проверьте код на детской площадке .

Inanc Gumus
источник
Однако это не будет работать над массивом, только над срезом
user1028741
@ user1028741 Нет, код работает для всех типов, включая массивы + Вы всегда можете взять кусок из массива array[:].
Inanc Gumus,
Но тип массива будет соответствовать его фактической емкости. [3] int не того же типа, что [2] int или [] int. Следовательно, если тип 't' на самом деле является массивом, соответствующий регистр не применяется.
user1028741
1
Я имею в виду, что ваш пример не будет работать с массивами. Я изменил его здесь: play.golang.org/p/DAsg_0aXz-r
user1028741
Вы не сможете легко заставить его работать, если вы в первую очередь не знаете тип своего параметра. Чтобы использовать свой трюк (array [:]), вам нужно будет использовать отражение, чтобы решить, что это массив, затем преобразовать его в массив, а затем сгенерировать его кусок. Вот почему я сказал, что он работает со срезом, а не с массивом. Понятно, что
приложив
4

есть одно исключение из поведения интерфейса {}, @ Джереми Уолл уже дал указатель. если переданные данные изначально определены как [] interface {}.

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

выход:

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]

попробуй это

Матус Крал
источник