діапазон інтерфейсу {}, в якому зберігається фрагмент


100

Враховуючи сценарій, коли у вас є функція, яка приймає 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)
        }
    }
}

Перейти на приклад Playground: http://play.golang.org/p/DNldAlNShB


Чому б замість функції не взяти [] інтерфейс {}? Ви намагаєтесь обробляти кілька складних типів?
Джеремі Стін

2
Так, це для системи шаблонування, тому інтерфейс {} може бути картою, структурою, фрагментом або масивом. У реальному коді є набагато більше тверджень, але я видалив їх із поста, щоб зробити проблему більш стислою.
Nucleon

1
Джеремі, рядок [] не є підтипом [] інтерфейсу {}, тому ви не можете викликати функцію func ([] інтерфейс {}) за допомогою рядка [] або [] int тощо. Було б непогано мати функцію в Go, щоб мати тип, що означає "фрагмент чогось", де ви можете потім перебирати елементи у вигляді інтерфейсу {}, але, на жаль, для цього вам потрібні роздуми.
Bjarke Ebert

@JeremyWall Ви не можете використовувати [] інтерфейс {}, щоб діяти як фрагмент. Ви можете звернутися сюди .
firelyu

Відповіді:


140

Ну, я використовував, 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))
        }
    }
}

Перейти на приклад Playground: http://play.golang.org/p/gQhCTiwPAq


26
Чудово працює, дякую. Єдине, що слід додати, це те, що s.Index(i)повертає reflect.Valueтак, у моєму випадку мені потрібно було вказати s.Index(i).Interface()фактичне значення.
Nucleon

1
Що б ви зробили, якби у вас був вказівник на фрагмент interface{}? наприклад moredata := &[]int{1,2,3}
Райан

1
Відповідаючи на моє запитання: Якщо у вас є вказівник на зріз замість зрізу, вам потрібно буде використовувати, Elem()щоб отримати базове значення. напр. reflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
Райан

що, якщо я хочу отримати значення поля інтерфейсу з фрагмента інтерфейсу без будь-якої структури. Я спробував це рішення, але обмежено отримання посилань на інтерфейс лише з фрагмента, а не фактичних значень полів.
Amandeep kaur,

ух велике спасибі, цей фокус заощадив мені багато часу (і головний біль!)
Ніколас Гарньє,

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

Ви не можете з легкістю змусити це працювати, якщо ви не знаєте тип параметра, перш за все. Для того, щоб скористатися вашим фокусом (масив [:]), вам доведеться використовувати відображення, щоб вирішити, що це масив, а потім передати його в масив - потім створити його фрагмент. Ось чому я сказав, що це працює на зрізі, а не на масиві. Очевидно, достатньо зусиль ви можете створити фрагмент із масиву ...
user1028741

4

є один виняток із поведінки інтерфейсу {}, @Jeremy Wall вже дав покажчик. якщо передані дані спочатку визначаються як [] інтерфейс {}.

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.