Go 中的泛型编程。避免硬编码类型断言
Generic Programming in Go. Avoiding hard coded type assertion
我正在编写通用缓存机制,我需要在结构中设置一些属性,只知道它们的 reflect.Type、属性名称和要在属性中设置的 reflect.Value,但我可以' 避免类型断言,这使我的代码不通用...
func main() {
addressNew := Address{"New Address description!"}
// In the real problem, i know the reflect.Type of value, but
// the struct came to me as a interface{}, just like this method
// Return many kinds of values from redis as interface{},
// (Customer, Order, Address, Product, SKU etc), in a generic way,
// but returns the reflect.Type of value also.
interfaceSomeValue := getMyValue()
fmt.Printf("%v", interfaceSomeValue)
fmt.Println("")
// This portion of code comes from my cache mechanism, that is a library
// used by other projects. My cache lib really can't know all others
// type structs to perform the type assertion, but the cache mechanism know
// the reflect.Type of the interface.
// If you try at this way, will cause a panic by try to access a FieldByName
// in a interface, because the Customer comes from getMyValue and
// becomes a interface{}, and now a type assertion is
// required -> http://play.golang.org/p/YA8U9_KzC9
newCustomerNewAttribute := SetAttribute(&interfaceSomeValue, "Local", interface{}(addressNew), reflect.TypeOf(Customer{}))
fmt.Printf("%v", newCustomerNewAttribute)
fmt.Println("")
}
func SetAttribute(object interface{}, attributeName string, attValue interface{}, objectType reflect.Type) interface{} {
if reflect.ValueOf(object).Kind() != reflect.Ptr {
panic("need a pointer")
}
value := reflect.ValueOf(object).Elem()
field := value.FieldByName(attributeName)
valueForAtt := reflect.ValueOf(attValue)
field.Set(valueForAtt)
return value.Interface()
}
Go Playground for the problem (works by hard coded type assertion)...
Go Playground for the problem (doesn't work with an unknown interface)
终于我找到了一种方法来做到这一点。按照下面的 Go Playground 和代码片段操作:
GO Playground: Avoiding Hard Coded Type Assertion
//new approach SetAttribute, without any hard coded type assertion, just based on objectType parameter
func SetAttribute(myUnknownTypeValue *interface{}, attributeName string, attValue interface{}, objectType reflect.Type) {
// create value for old val
oldValue := reflect.ValueOf(*myUnknownTypeValue)
//create a new value, based on type
newValue := reflect.New(objectType).Elem()
// set the old value to the new one, making the
// implicit type assertion, not hard coding that.
newValue.Set(oldValue)
//set value attribute to the new struct, copy of the old one
field := newValue.FieldByName(attributeName)
valueForAtt := reflect.ValueOf(attValue)
field.Set(valueForAtt)
//capture the new value from reflect.Value
newValInterface := newValue.Interface()
//set the new value to the pointer
*myUnknownTypeValue = newValInterface
}
我正在编写通用缓存机制,我需要在结构中设置一些属性,只知道它们的 reflect.Type、属性名称和要在属性中设置的 reflect.Value,但我可以' 避免类型断言,这使我的代码不通用...
func main() {
addressNew := Address{"New Address description!"}
// In the real problem, i know the reflect.Type of value, but
// the struct came to me as a interface{}, just like this method
// Return many kinds of values from redis as interface{},
// (Customer, Order, Address, Product, SKU etc), in a generic way,
// but returns the reflect.Type of value also.
interfaceSomeValue := getMyValue()
fmt.Printf("%v", interfaceSomeValue)
fmt.Println("")
// This portion of code comes from my cache mechanism, that is a library
// used by other projects. My cache lib really can't know all others
// type structs to perform the type assertion, but the cache mechanism know
// the reflect.Type of the interface.
// If you try at this way, will cause a panic by try to access a FieldByName
// in a interface, because the Customer comes from getMyValue and
// becomes a interface{}, and now a type assertion is
// required -> http://play.golang.org/p/YA8U9_KzC9
newCustomerNewAttribute := SetAttribute(&interfaceSomeValue, "Local", interface{}(addressNew), reflect.TypeOf(Customer{}))
fmt.Printf("%v", newCustomerNewAttribute)
fmt.Println("")
}
func SetAttribute(object interface{}, attributeName string, attValue interface{}, objectType reflect.Type) interface{} {
if reflect.ValueOf(object).Kind() != reflect.Ptr {
panic("need a pointer")
}
value := reflect.ValueOf(object).Elem()
field := value.FieldByName(attributeName)
valueForAtt := reflect.ValueOf(attValue)
field.Set(valueForAtt)
return value.Interface()
}
Go Playground for the problem (works by hard coded type assertion)...
Go Playground for the problem (doesn't work with an unknown interface)
终于我找到了一种方法来做到这一点。按照下面的 Go Playground 和代码片段操作:
GO Playground: Avoiding Hard Coded Type Assertion
//new approach SetAttribute, without any hard coded type assertion, just based on objectType parameter
func SetAttribute(myUnknownTypeValue *interface{}, attributeName string, attValue interface{}, objectType reflect.Type) {
// create value for old val
oldValue := reflect.ValueOf(*myUnknownTypeValue)
//create a new value, based on type
newValue := reflect.New(objectType).Elem()
// set the old value to the new one, making the
// implicit type assertion, not hard coding that.
newValue.Set(oldValue)
//set value attribute to the new struct, copy of the old one
field := newValue.FieldByName(attributeName)
valueForAtt := reflect.ValueOf(attValue)
field.Set(valueForAtt)
//capture the new value from reflect.Value
newValInterface := newValue.Interface()
//set the new value to the pointer
*myUnknownTypeValue = newValInterface
}