Jak określić „prawdziwy” typ wartości interfejsu {}?


120

Nie znalazłem dobrego źródła do używania interface{}typów. Na przykład

package main

import "fmt"

func weirdFunc(i int) interface{} {
    if i == 0 {
        return "zero"
    }
    return i
}
func main() {
    var i = 5
    var w = weirdFunc(5)

    // this example works!
    if tmp, ok := w.(int); ok {
        i += tmp
    }

    fmt.Println("i =", i)
}

Czy znasz dobre wprowadzenie do korzystania z Go interface{}?

szczegółowe pytania:

  • jak uzyskać „prawdziwy” typ w?
  • czy istnieje sposób, aby uzyskać ciąg reprezentujący typ?
  • czy istnieje sposób na użycie reprezentacji ciągu typu do konwersji wartości?

Odpowiedzi:


98

Twój przykład działa. Oto uproszczona wersja.

package main

import "fmt"

func weird(i int) interface{} {
    if i < 0 {
        return "negative"
    }
    return i
}

func main() {
    var i = 42
    if w, ok := weird(7).(int); ok {
        i += w
    }
    if w, ok := weird(-100).(int); ok {
        i += w
    }
    fmt.Println("i =", i)
}

Output:
i = 49

Używa asercji typu .


masz całkowitą rację! dzięki! czy masz wgląd w reprezentacje typów w postaci ciągów znaków?
cc młody

12
Sprawdź reflect.TypeOf.
Dmitri Goldring

@DmitriGoldring To przynajmniej odpowiada na pytanie w tytule tematów. Ta odpowiedź nie. Dziękuję Ci bardzo.
C4d

129

Możesz także zmienić typ:

switch v := myInterface.(type) {
case int:
    // v is an int here, so e.g. v + 1 is possible.
    fmt.Printf("Integer: %v", v)
case float64:
    // v is a float64 here, so e.g. v + 1.0 is possible.
    fmt.Printf("Float64: %v", v)
case string:
    // v is a string here, so e.g. v + " Yeah!" is possible.
    fmt.Printf("String: %v", v)
default:
    // And here I'm feeling dumb. ;)
    fmt.Printf("I don't know, ask stackoverflow.")
}

Dziękuję za to. ale nadal nie całkiem. w tym przykładzie, jak zmusić var ​​w do int?
cc młody

3
Przykład Mue robi to samo, ale z przełącznikiem typu zamiast instrukcji if. W „przypadku int” „v” będzie liczbą całkowitą. w „przypadku float64”, „v” będzie zmiennym float64 itd.
jimt

dobrze. zapomniał o składni var. (typ), która jest podstępna i fajna
cc young

51

Możesz użyć refleksji ( reflect.TypeOf()), aby uzyskać typ czegoś, a wartość, którą daje ( Type), ma reprezentację łańcuchową ( Stringmetodę), którą możesz wydrukować.


10
A jeśli chcesz tylko uzyskać ciąg lub typ (np. Do drukowania w domyślnym bloku linku przełączania typu w odpowiedzi Mue, możesz po prostu użyć fmtformatu "% T" zamiast bezpośredniego używania reflect.
Dave C

16

Oto przykład dekodowania mapy ogólnej przy użyciu przełącznika i odbicia, więc jeśli nie dopasujesz typu, użyj odbicia, aby to rozgryźć, a następnie dodaj typ następnym razem.

var data map[string]interface {}

...

for k, v := range data {
    fmt.Printf("pair:%s\t%s\n", k, v)   

    switch t := v.(type) {
    case int:
        fmt.Printf("Integer: %v\n", t)
    case float64:
        fmt.Printf("Float64: %v\n", t)
    case string:
        fmt.Printf("String: %v\n", t)
    case bool:
        fmt.Printf("Bool: %v\n", t)
    case []interface {}:
        for i,n := range t {
            fmt.Printf("Item: %v= %v\n", i, n)
        }
    default:
        var r = reflect.TypeOf(t)
        fmt.Printf("Other:%v\n", r)             
    }
}

6

Przełączniki typu mogą być również używane z elementami odblaskowymi:

var str = "hello!"
var obj = reflect.ValueOf(&str)

switch obj.Elem().Interface().(type) {
case string:
    log.Println("obj contains a pointer to a string")
default:
    log.Println("obj contains something else")
}

2

Mam zamiar zaoferować sposób na zwrócenie wartości logicznej w oparciu o przekazanie argumentu odbicia Kinds do odbiornika typu lokalnego (ponieważ nie mogłem znaleźć czegoś takiego).

Najpierw deklarujemy nasz anonimowy typ typu Reflect.

type AnonymousType reflect.Value

Następnie dodajemy konstruktora dla naszego typu lokalnego AnonymousType, który może przyjąć dowolny potencjalny typ (jako interfejs):

func ToAnonymousType(obj interface{}) AnonymousType {
    return AnonymousType(reflect.ValueOf(obj))
}

Następnie dodajemy funkcję do naszej struktury AnonymousType, która sprawdza przed reflektorem.

func (a AnonymousType) IsA(typeToAssert reflect.Kind) bool {
    return typeToAssert == reflect.Value(a).Kind()
}

To pozwala nam zadzwonić do następujących:

var f float64 = 3.4

anon := ToAnonymousType(f)

if anon.IsA(reflect.String) {
    fmt.Println("Its A String!")
} else if anon.IsA(reflect.Float32) {
    fmt.Println("Its A Float32!")
} else if anon.IsA(reflect.Float64) {
    fmt.Println("Its A Float64!")
} else {
    fmt.Println("Failed")
}

Dłuższą, działającą wersję można zobaczyć tutaj: https://play.golang.org/p/EIAp0z62B7


1

Istnieje wiele sposobów uzyskania ciągu reprezentującego typ. Przełączniki mogą być również używane z typami użytkowników:

var user interface{}
user = User{name: "Eugene"}

// .(type) can only be used inside a switch
switch v := user.(type) {
case int:
    // Built-in types are possible (int, float64, string, etc.)
    fmt.Printf("Integer: %v", v)
case User:
    // User defined types work as well  
    fmt.Printf("It's a user: %s\n", user.(User).name)
}

// You can use reflection to get *reflect.rtype
userType := reflect.TypeOf(user)
fmt.Printf("%+v\n", userType)

// You can also use %T to get a string value
fmt.Printf("%T", user)

// You can even get it into a string
userTypeAsString := fmt.Sprintf("%T", user)

if userTypeAsString == "main.User" {
    fmt.Printf("\nIt's definitely a user")
}

Link do placu zabaw: https://play.golang.org/p/VDeNDUd9uK6

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.