问题
I need a way to dynamically cast a struct/interface back to its original object. I can add methods / functions inside. basically I need something like this:
MyStruct => Interface{} => MyStruct
When on the final conversion I don't know anything about the original struct besides what come inside the struct, so I can't just so:
a.(MyStruct)
回答1:
No: as mentioned in this thread
Go is neither covariant nor contravariant. Types are either equal or they aren't.
You have to either take the structs apart and deal with the pieces, or use reflection.
Type assertions are only "assertions", not "coercions" of any kind.
See also this thread, which reminds us that:
- A pointer is one kind of type.
- A struct is another kind of type.
- An integer is another kind of type.
- A floating point number is another kind of type.
- A boolean is another kind of type.
The principle of an interface concerns the methods attached to a type
T
, not what typeT
is.An interface type is defined by a set of methods.
Any value that implements the methods can be assigned to an interface value of that type.
That would make the conversion from interface to concrete type quite difficult to do.
回答2:
You need to know at least the possible types it could be. There's a couple cases, 1. You think you might know what it is. 2. You have a list of possible types it could be, 3. Your code knows nothing about the underlying types.
- If you think you know it, you can use type assertion to convert back to the original struct type.
...
package main
import (
"fmt"
)
type MyStruct struct {
Thing string
}
func (s *MyStruct) Display() {
fmt.Println(s.Thing)
}
type Thingable interface {
Display()
}
func main() {
s := &MyStruct{
Thing: "Hello",
}
// print as MyThing
s.Display()
var thinger Thingable
thinger = s
// print as thingable interface
thinger.Display()
// convert thinger back to MyStruct
s2 := thinger.(*MyStruct) // this is "type assertion", you're asserting that thinger is a pointer to MyStruct. This will panic if thinger is not a *MyStruct
s2.Display()
}
You can see this in action here: https://play.golang.org/p/rL12Lrpqsyu
Note if you want to test the type without panicking if you're wrong, do s2, ok := thinger.(*MyStruct)
. ok will be true if it was successful and false otherwise.
- if you want to test your interface variable against a bunch of types, use a switch: (scroll to bottom)
...
package main
import (
"fmt"
"reflect"
)
type MyStruct struct {
Thing string
}
type MyStruct2 struct {
Different string
}
func (s *MyStruct) Display() {
fmt.Println(s.Thing)
}
func (s *MyStruct2) Display() {
fmt.Println(s.Different)
}
type Thingable interface {
Display()
}
func main() {
s := &MyStruct{
Thing: "Hello",
}
// print as MyThing
s.Display()
var thinger Thingable
thinger = s
// print as thingable interface
thinger.Display()
// try to identify thinger
switch t := thinger.(type) {
case *MyStruct:
fmt.Println("thinger is a *MyStruct. Thing =", t.Thing)
case *MyStruct2:
fmt.Println("thinger is a *MyStruct2. Different =", t.Different)
default:
fmt.Println("thinger is an unknown type:", reflect.TypeOf(thinger))
}
}
You can try that out here https://play.golang.org/p/7NEbwB5j6Is
- If you really don't know anything about the underlying types, you'll have to expose the things you need through interface functions and call those. Chances are you can do this without knowing anything about the underlying type.
来源:https://stackoverflow.com/questions/41660857/how-to-cast-interface-back-into-its-original-struct