スライスを格納するインターフェース上の範囲{}


96

あなたが受け入れる関数を持っているシナリオを考えると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 Playgroundの例:http : //play.golang.org/p/DNldAlNShB


代わりに、関数に[]インターフェイス{}を取らせないのはなぜですか?複数の複合型を処理しようとしていますか?
Jeremy Wall

2
はい、これはテンプレートシステム用であるため、インターフェイス{}はマップ、構造体、スライス、または配列にすることができます。実際のコードにはさらに多くのcaseステートメントがありますが、問題をより簡潔にするためにそれらを投稿から削除しました。
Nucleon

1
Jeremy、[] stringは[] interface {}のサブタイプではないため、[] stringや[] intなどでfunc([] interface {})関数を呼び出すことはできません。 Goに「何かのスライス」を意味するタイプを持つ機能があります。この場合、要素をインターフェースとして反復できます{}が、残念ながらそのためのリフレクションが必要です。
Bjarke Ebert

@JeremyWall [] interface {}をスライスとして使用することはできません。こちらをご参照ください
firelyu

回答:


137

さて、私が使用reflect.ValueOfし、それはスライスがある場合は、呼び出すことができますLen()し、Index()取得する値のlenインデックスでスライスし、要素のを。これを行うためにrangeオペレーションを使用することはできないと思います。

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


23
素晴らしい作品、ありがとうございます。追加する唯一のものはそれをs.Index(i)返すことですreflect.Valueので、私の場合、私s.Index(i).Interface()は実際の値を参照する必要がありました。
Nucleon

1
スライスへのポインタがある場合はどうしますinterface{}か?例 moredata := &[]int{1,2,3}
Ryan Walls

1
私の質問への回答:スライスではなくスライスへのポインターがある場合はElem()、基になる値を取得するために使用する必要があります。例 reflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
Ryan Walls

構造体なしでインターフェイスのスライスからインターフェイスフィールドの値を取得したい場合 私はこの解決策を試しましたが、フィールドの実際の値ではなくスライスからのみインターフェース参照を取得することに制限されています。
アマンディープカウル

おかげで、そのトリックは私に多くの時間を節約しました(そして頭痛の種です!)
Nicolas Garnier

26

期待するタイプがわかっている場合は、リフレクションを使用する必要はありません。あなたは使うことができますようにタイプスイッチ

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[:]
イナングムス

ただし、アレイのタイプは実際の容量になります。[3] intは[2] intまたは[] intと同じ型ではありません。したがって、「t」のタイプが実際に配列の場合、関連するケースは適用されません。
user1028741

つまり、あなたの例は配列では機能しません。ここで変更しました:play.golang.org/p/DAsg_0aXz-r
user1028741

そもそもパラメータのタイプがわからない場合、簡単に機能させることはできません。トリック(array [:])を使用するには、リフレクションを使用してそれが配列であることを確認し、次に配列にキャストして、そのスライスを生成する必要があります。それが、配列ではなくスライスで機能すると私が言った理由です。明らかに、十分な労力でアレイからスライスを作成できます...
user1028741

4

インターフェース{}の動作には1つの例外があります。@ Jeremy Wallはすでにポインタを提供しています。渡されたデータが最初に[] 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]

やってみよう

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.