Wie bestimme ich den "echten" Typ eines Schnittstellenwerts {}?

120

Ich habe keine gute Ressource für die Verwendung von interface{}Typen gefunden. Beispielsweise

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

Kennen Sie eine gute Einführung in die Verwendung von Go's? interface{} ?

spezifische Fragen:

  • Wie bekomme ich den "echten" Typ von w?
  • Gibt es eine Möglichkeit, die Zeichenfolgendarstellung eines Typs abzurufen?
  • Gibt es eine Möglichkeit, die Zeichenfolgendarstellung eines Typs zum Konvertieren eines Werts zu verwenden?
cc jung
quelle

Antworten:

97

Ihr Beispiel funktioniert. Hier ist eine vereinfachte Version.

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

Es werden Typzusicherungen verwendet .

peterSO
quelle
Du hast absolut recht! Vielen Dank! Haben Sie einen Einblick in Typ-String-Darstellungen von Typen?
cc junge
12
Auschecken reflect.TypeOf.
Dmitri Goldring
@DmitriGoldring Das beantwortet zumindest die Frage im Thementitel. Diese Antwort nicht. Vielen Dank.
C4d
128

Sie können auch Typschalter ausführen:

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.")
}
Thema
quelle
Danke für das. aber immer noch nicht ganz da. Wie kann ich im Beispiel var w in ein int zwingen?
CC junge
3
Mues Beispiel macht dasselbe, jedoch in einem Typwechsel anstelle einer if-Anweisung. Im 'case int' ist 'v' eine ganze Zahl. in 'case float64' ist 'v' ein float64 usw.
jimt
richtig. hatte Syntax var. (Typ) vergessen, die hinterhältig und cool ist
cc jung
51

Sie können Reflection ( reflect.TypeOf()) verwenden, um den Typ von etwas abzurufen, und der darin angegebene Wert ( Type) enthält eine Zeichenfolgendarstellung ( StringMethode), die Sie drucken können.

newacct
quelle
10
Und wenn Sie nur eine Zeichenfolge oder einen Typ erhalten möchten (z. B. zum Drucken im Standardblock eines Typwechsel-Links in Mues Antwort , können Sie einfach fmtdas "% T" -Format verwenden, anstatt es direkt zu verwenden reflect.
Dave C
16

Hier ist ein Beispiel für das Dekodieren einer generischen Karte mit Schalter und Reflexion. Wenn Sie also nicht mit dem Typ übereinstimmen, verwenden Sie Reflexion, um dies herauszufinden, und fügen Sie den Typ beim nächsten Mal hinzu.

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
quelle
6

Typschalter können auch mit Reflexionsmaterial verwendet werden:

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
quelle
2

Ich werde eine Möglichkeit anbieten, einen Booleschen Wert zurückzugeben, indem ein Argument eines Reflection Kinds an einen lokalen Typempfänger übergeben wird (weil ich so etwas nicht finden konnte).

Zuerst deklarieren wir unseren anonymen Typ vom Typ Reflect.Value:

type AnonymousType reflect.Value

Dann fügen wir einen Builder für unseren lokalen Typ AnonymousType hinzu, der jeden möglichen Typ (als Schnittstelle) aufnehmen kann:

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

Dann fügen wir eine Funktion für unsere AnonymousType-Struktur hinzu, die gegen ein Reflect behauptet. Art:

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

Dies ermöglicht es uns, Folgendes aufzurufen:

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

Eine längere, funktionierende Version finden Sie hier: https://play.golang.org/p/EIAp0z62B7

daino3
quelle
1

Es gibt mehrere Möglichkeiten, eine Zeichenfolgendarstellung eines Typs abzurufen. Schalter können auch mit Benutzertypen verwendet werden:

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 zu einem Spielplatz: https://play.golang.org/p/VDeNDUd9uK6

Eugene Kulabuhov
quelle