¿Cómo determinar el tipo "real" de un valor de {} interfaz?

120

No he encontrado un buen recurso para usar interface{}tipos. Por ejemplo

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

¿Conoce una buena introducción al uso de Go's interface{}?

preguntás especificas:

  • ¿Cómo obtengo el tipo de w "real"?
  • ¿Hay alguna forma de obtener la representación de cadena de un tipo?
  • ¿Hay alguna forma de utilizar la representación de cadena de un tipo para convertir un valor?
cc joven
fuente

Respuestas:

98

Tu ejemplo funciona. Aquí tienes una versión simplificada.

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

Utiliza aserciones de tipo .

peterSO
fuente
¡Estás absolutamente en lo correcto! ¡Gracias! ¿Tiene alguna idea sobre los tipos de representaciones de cadenas de tipos?
cc young
12
Echa un vistazo reflect.TypeOf.
Dmitri Goldring
@DmitriGoldring Eso al menos responde a la pregunta en el título de los temas. Esta respuesta no. Muchas gracias.
C4d
129

También puede hacer cambios de 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.")
}
ellos
fuente
gracias por eso. pero todavía no del todo. en el ejemplo, ¿cómo puedo coaccionar var w en un int?
cc young
3
El ejemplo de Mue hace lo mismo, pero en un cambio de tipo en lugar de una declaración if. En el 'caso int', 'v' será un número entero. en 'case float64', 'v' será un float64, etc.
jimt
Derecha. había olvidado la var. de sintaxis (tipo), que es furtiva y genial
cc young
51

Puede usar reflect ( reflect.TypeOf()) para obtener el tipo de algo, y el valor que da ( Type) tiene una representación de cadena ( Stringmétodo) que puede imprimir.

newacct
fuente
10
Y si solo desea obtener una cadena o un tipo (por ejemplo, para imprimir en el bloque predeterminado de un enlace de cambio de tipo en la respuesta de Mue, puede usar fmtel formato "% T" en lugar de usarlo directamente reflect.
Dave C
16

Aquí hay un ejemplo de decodificación de un mapa genérico usando tanto el interruptor como la reflexión, así que si no coincide con el tipo, use la reflexión para averiguarlo y luego agregue el tipo la próxima vez.

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

Los interruptores de tipo también se pueden usar con material de reflexión:

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

Voy a ofrecer una forma de devolver un booleano basado en pasar un argumento de un tipo de reflexión a un receptor de tipo local (porque no pude encontrar nada como esto).

Primero, declaramos nuestro tipo anónimo de tipo reflect.

type AnonymousType reflect.Value

Luego agregamos un constructor para nuestro tipo local AnonymousType que puede tomar cualquier tipo potencial (como interfaz):

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

Luego agregamos una función para nuestra estructura AnonymousType que afirma contra un reflect.

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

Esto nos permite llamar a lo siguiente:

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

Puede ver una versión más larga y funcional aquí: https://play.golang.org/p/EIAp0z62B7

daino3
fuente
1

Hay varias formas de obtener una representación de cadena de un tipo. Los interruptores también se pueden usar con tipos de usuario:

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

Enlace a un parque infantil: https://play.golang.org/p/VDeNDUd9uK6

Eugene Kulabuhov
fuente