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


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


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

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


Однако это не будет работать над массивом, только над срезом
— 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]

попробуй это

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.