phạm vi trên giao diện {} lưu trữ một lát


96

Đưa ra tình huống mà bạn có một hàm chấp nhận t interface{}. Nếu nó được xác định rằng đó tlà một lát cắt, làm thế nào để tôi rangevượt qua lát cắt đó?

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

Ví dụ về Go Playground: http://play.golang.org/p/DNldAlNShB


Tại sao không sử dụng hàm [] giao diện {} để thay thế? Bạn đang cố gắng xử lý nhiều loại phức tạp?
Jeremy Wall

2
Đúng, nó dành cho hệ thống tạo khuôn mẫu nên giao diện {} có thể là một bản đồ, cấu trúc, lát cắt hoặc mảng. Trong mã thực có rất nhiều câu lệnh tình huống khác, nhưng tôi đã xóa chúng khỏi bài đăng để làm cho vấn đề ngắn gọn hơn.
Nucleon

1
Jeremy, một chuỗi [] không phải là một kiểu con của [] interface {}, vì vậy bạn không thể gọi hàm func ([] interface {}) bằng một chuỗi [] hoặc [] int, v.v. Sẽ rất tuyệt nếu có một tính năng trong Go để có một loại có nghĩa là "phần nào đó", nơi bạn có thể lặp lại các phần tử dưới dạng giao diện {}, nhưng rất tiếc, bạn cần phản ánh điều đó.
Bjarke Ebert

@JeremyWall Bạn không thể sử dụng [] giao diện {} để hoạt động như một lát cắt. Bạn có thể tham khảo tại đây .
firelyu

Câu trả lời:


137

Tôi đã sử dụng reflect.ValueOfvà sau đó nếu nó là một lát cắt, bạn có thể gọi Len()Index()trên giá trị để lấylen lát cắt và phần tử tại một chỉ mục. Tôi không nghĩ rằng bạn sẽ có thể sử dụng hoạt động phạm vi để làm điều này.

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

Ví dụ về Go Playground: http://play.golang.org/p/gQhCTiwPAq


23
Hoạt động tuyệt vời, cảm ơn bạn. Điều duy nhất để thêm sẽ s.Index(i)trả về một reflect.Valuevì vậy trong trường hợp của tôi, tôi cần s.Index(i).Interface()tham chiếu giá trị thực tế.
Nucleon

1
Bạn sẽ làm gì nếu bạn có một con trỏ đến một lát cắt trong đó interface{}? ví dụ moredata := &[]int{1,2,3}
Ryan Walls

1
Trả lời câu hỏi của tôi: Nếu bạn có một con trỏ đến một lát cắt thay vì một lát cắt, bạn sẽ cần sử dụng Elem()để nhận giá trị cơ bản. ví dụ reflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
Ryan Walls

điều gì sẽ xảy ra nếu tôi muốn nhận giá trị của trường giao diện từ phần giao diện mà không có bất kỳ cấu trúc nào. Tôi đã thử giải pháp này nhưng bị hạn chế là chỉ nhận được tham chiếu giao diện từ lát không phải giá trị thực của các trường.
Amandeep kaur, 27/09/17

woh, cảm ơn rất nhiều, thủ thuật đó đã giúp tôi tiết kiệm rất nhiều thời gian (và đau đầu!)
Nicolas Garnier

26

Bạn không cần phải sử dụng phản xạ nếu bạn biết loại nào mong đợi. Bạn có thể sử dụng một công tắc loại , như sau:

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

Kiểm tra mã trên sân chơi .


Tuy nhiên, điều này sẽ không hoạt động trên một mảng, chỉ trên một lát
dùng1028741

@ user1028741 Không, mã hoạt động cho mọi loại bao gồm cả mảng + Bạn luôn có thể lấy một lát từ một mảng array[:].
Inanc Gumus

Nhưng kiểu của mảng sẽ tương ứng với dung lượng thực của nó. [3] int không cùng kiểu với [2] int hoặc [] int. Do đó, nếu kiểu 't' thực sự là một mảng thì trường hợp liên quan sẽ không áp dụng.
user1028741

Ý tôi là ví dụ của bạn sẽ không hoạt động trên mảng. Tôi đã thay đổi nó ở đây: play.golang.org/p/DAsg_0aXz-r
user1028741 Ngày

Bạn không thể dễ dàng làm cho nó hoạt động, nếu bạn không biết loại thông số của mình ngay từ đầu. Để sử dụng thủ thuật của bạn (mảng [:]), bạn sẽ phải sử dụng phản xạ để quyết định rằng đó là một mảng, sau đó ép kiểu sang mảng - sau đó tạo một lát cắt của nó. Đó là lý do tại sao tôi đã nói rằng nó hoạt động trên một lát, không phải trên một mảng. Rõ ràng với đủ nỗ lực, bạn có thể tạo ra một lát cắt ra khỏi mảng ...
user1028741 Ngày

4

có một ngoại lệ từ cách giao diện {} hoạt động, @Jeremy Wall đã đưa ra con trỏ. nếu dữ liệu được truyền vào được xác định là [] giao diện {} ban đầu.

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

đầu ra:

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]

thử nó ra

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.