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


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.