bir dilim depolayan arayüz üzerinden {} aralığı


100

Kabul eden bir işleve sahip olduğunuz senaryo göz önüne alındığında t interface{}. tBir dilim olduğu belirlenirse, o dilimi nasıl rangeaşarım?

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 Örneği: http://play.golang.org/p/DNldAlNShB


Neden işlev bunun yerine bir [] arayüz {} almasın? Birden çok karmaşık türü ele almaya mı çalışıyorsunuz?
Jeremy Wall

2
Evet, bu bir şablonlama sistemi içindir, bu nedenle arayüz {} bir harita, yapı, dilim veya dizi olabilir. Gerçek kodda çok daha fazla vaka ifadesi var, ancak sorunu daha kısa hale getirmek için bunları gönderiden kaldırdım.
Nucleon

1
Jeremy, bir [] dize, [] arabirimin {} alt türü değildir, bu nedenle [] dizesi veya [] int ile bir func ([] arabirim {}) işlevini çağıramazsınız, vb. Go'da "bir şeyin dilimi" anlamına gelen bir türe sahip olan bir özelliğe sahip olun, burada daha sonra arayüz olarak öğeler üzerinde yineleme yapabilirsiniz {}, ancak maalesef bunun için düşünmeye ihtiyacınız var.
Bjarke Ebert

@JeremyWall Dilim olarak hareket etmek için [] arayüzünü {} kullanamazsınız. Buradan başvurabilirsiniz .
firelyu

Yanıtlar:


140

Ben de kullanılan reflect.ValueOfve o zaman bir dilim ise arayabileceğiniz Len()ve Index()almak için değeri lenbir endeksinde dilim ve elemanın. Bunu yapmak için menzili kullanabileceğinizi sanmıyorum.

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


26
Harika çalışıyor, teşekkürler. Eklenecek tek şey, s.Index(i)a döndürmesidir, reflect.Valuebu yüzden benim durumumda s.Index(i).Interface()gerçek değere başvurmam gerekiyor .
Nucleon

1
Bir dilim için bir işaretçiniz olsaydı ne yapardınız interface{}? örneğin moredata := &[]int{1,2,3}
Ryan Walls

1
Soruma cevap vermek: Bir dilim yerine bir dilim için bir işaretçiniz varsa Elem(), temel değeri elde etmek için kullanmanız gerekecek . örneğin reflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
Ryan Walls

ya arayüz alanının değerini herhangi bir yapı olmadan arayüz diliminden almak istersem? Bu çözümü denedim, ancak alanların gerçek değerlerini değil, sadece dilimden arayüz referansı almak sınırlıdır.
Amandeep kaur

woh çok teşekkürler, bu numara bana çok zaman kazandırdı (ve baş ağrısını!)
Nicolas Garnier

27

Hangi türleri bekleyeceğinizi biliyorsanız, derinlemesine düşünmenize gerek yoktur. Şunun gibi bir tür anahtarı kullanabilirsiniz :

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

Oyun alanındaki kodu kontrol edin .


Bu bir dizi üzerinde çalışmayacak, sadece bir dilim üzerinde
çalışacak

@ user1028741 Hayır, kod diziler dahil her tür için çalışır + Bir diziden her zaman bir dilim alabilirsiniz array[:].
İnanç Gümüş

Ancak bir dizinin türü, gerçek kapasitesiyle birlikte olacaktır. [3] int, [2] int veya [] int ile aynı türde değil. Bu nedenle, 't' türü aslında bir dizi ise, ilgili durum geçerli olmayacaktır.
user1028741

1
Demek istediğim, örneğiniz diziler üzerinde çalışmayacak. Burada değiştirdim: play.golang.org/p/DAsg_0aXz-r
user1028741

İlk etapta parametrenizin türünü bilmiyorsanız, kolayca çalışmasını sağlayamazsınız. Numaranızı (dizi [:]) kullanmak için, bunun bir dizi olduğuna karar vermek için yansımayı kullanmanız, ardından diziye çevirmeniz ve ardından bir dilim oluşturmanız gerekir. Bu yüzden bir dizi üzerinde değil, bir dilim üzerinde çalıştığını söyledim. Açıkçası, yeterince çabayla diziden bir dilim üretebilirsiniz ...
user1028741

4

arayüzün {} davranış biçiminden bir istisna vardır, @ Jeremy Wall zaten işaret etti. aktarılan veriler başlangıçta [] arayüz {} olarak tanımlanmışsa.

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

çıktı:

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]

denemek

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.