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?
cc młody
źródło

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 .

peterSO
źródło
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.")
}
motyw
źródło
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ć.

newacct
źródło
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)             
    }
}
h4ck3rm1k3
źródło
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")
}
Nikolai Koudelia
źródło
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

daino3
źródło
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

Eugene Kulabuhov
źródło