Come determinare il tipo "reale" di un valore {} di interfaccia?


120

Non ho trovato una buona risorsa per utilizzare i interface{}tipi. Per esempio

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

Conosci una buona introduzione all'uso di Go's interface{}?

domande specifiche:

  • come ottengo il tipo "reale" di w?
  • c'è un modo per ottenere la rappresentazione di stringa di un tipo?
  • c'è un modo per utilizzare la rappresentazione di stringa di un tipo per convertire un valore?

Risposte:


98

Il tuo esempio funziona. Ecco una versione semplificata.

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

Utilizza le asserzioni di tipo .


hai assolutamente ragione! Grazie! hai qualche idea sui tipi rappresentazioni di stringhe di tipi?
cc giovane

12
Controlla reflect.TypeOf.
Dmitri Goldring

@DmitriGoldring che almeno risponde alla domanda nel titolo degli argomenti. Questa risposta no. Grazie mille.
C4d

129

Puoi anche fare interruttori di tipo:

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

grazie per questo. ma ancora non del tutto lì. nell'esempio, come costringere var w in un int?
cc giovane

3
L'esempio di Mue fa la stessa cosa, ma in un interruttore di tipo invece di un'istruzione if. Nel 'case int', 'v' sarà un numero intero. in 'case float64', 'v' sarà un float64, ecc.
jimt

giusto. aveva dimenticato la sintassi var. (tipo), che è subdola e interessante
cc young,

51

Puoi usare reflection ( reflect.TypeOf()) per ottenere il tipo di qualcosa, e il valore che dà ( Type) ha una rappresentazione di stringa ( Stringmetodo) che puoi stampare.


10
E se vuoi solo ottenere una stringa o un tipo (ad esempio per la stampa nel blocco predefinito di un collegamento di cambio di tipo nella risposta di Mue puoi semplicemente usare fmtil formato "% T" invece di usare direttamente reflect.
Dave C

16

Di seguito è riportato un esempio di decodifica di una mappa generica utilizzando sia l'interruttore che la riflessione, quindi se non si abbina il tipo, utilizzare la riflessione per capirlo e quindi aggiungere il tipo la volta successiva.

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

Gli interruttori di tipo possono essere utilizzati anche con elementi di riflessione:

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

Offro un modo per restituire un booleano basato sul passaggio di un argomento di una riflessione Kinds a un ricevitore di tipo locale (perché non sono riuscito a trovare nulla di simile).

Innanzitutto, dichiariamo il nostro tipo anonimo di tipo riflettere. Valore:

type AnonymousType reflect.Value

Quindi aggiungiamo un builder per il nostro tipo locale AnonymousType che può accettare qualsiasi tipo potenziale (come interfaccia):

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

Quindi aggiungiamo una funzione per la nostra struttura AnonymousType che asserisce contro una riflessione.

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

Questo ci permette di chiamare quanto segue:

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

Può vedere una versione più lunga e funzionante qui: https://play.golang.org/p/EIAp0z62B7


1

Esistono diversi modi per ottenere una rappresentazione di stringa di un tipo. Gli interruttori possono essere utilizzati anche con i tipi di utente:

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

Collegamento a un parco giochi: https://play.golang.org/p/VDeNDUd9uK6

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.