在Go语言中的泛型编程。避免硬编码的类型断言。

huangapple go评论87阅读模式
英文:

Generic Programming in Go. Avoiding hard coded type assertion

问题

我正在编写一个通用缓存机制,并且需要在一个结构体中设置一些属性,只知道它们的reflect.Type、属性名称和要设置的reflect.Value,但是我无法避免类型断言,这使得我的代码不够通用...

func main() {
    addressNew := Address{"新地址描述!"}

    // 在实际问题中,我知道值的reflect.Type,但是
    // 结构体以interface{}的形式传递给我,就像这个方法一样
    // 以通用的方式从redis返回多种类型的值,
    // (Customer, Order, Address, Product, SKU等),但也返回值的reflect.Type。
    interfaceSomeValue := getMyValue()

    fmt.Printf("%v", interfaceSomeValue)
    fmt.Println("")

    // 这部分代码来自我的缓存机制,它是其他项目使用的库
    // 我的缓存库真的不知道所有其他类型的结构体来执行类型断言,
    // 但是缓存机制知道接口的reflect.Type。
    // 如果你尝试这种方式,将会导致尝试在接口中访问FieldByName时发生panic,
    // 因为Customer来自getMyValue并且变成了interface{},
    // 现在需要进行类型断言 -> 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("需要一个指针")
    }

    value := reflect.ValueOf(object).Elem()
    field := value.FieldByName(attributeName)
    valueForAtt := reflect.ValueOf(attValue)
    field.Set(valueForAtt)
    return value.Interface()
}

问题的Go Playground(通过硬编码的类型断言解决)

问题的Go Playground(对于未知接口无法工作)

英文:

I'm programming a generic cache mechanism and i need to set some attributes in a struct knowing only their reflect.Type, attribute name and reflect.Value to be setted in the attribute, but i can't avoid the type assertion, that makes my code not generic...

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)

答案1

得分: -1

最后我找到了一种方法来实现这个。请按照下面的Go Playground和代码片段进行操作:

GO Playground: 避免硬编码类型断言

//新的方法SetAttribute,不需要任何硬编码的类型断言,只需基于objectType参数
func SetAttribute(myUnknownTypeValue *interface{}, attributeName string, attValue interface{}, objectType reflect.Type) {

	// 为旧值创建值
	oldValue := reflect.ValueOf(*myUnknownTypeValue)

	// 基于类型创建一个新值
	newValue := reflect.New(objectType).Elem()
	// 将旧值设置为新值,进行隐式类型断言,而不是硬编码
	newValue.Set(oldValue)

	// 将值属性设置为新结构体的副本
	field := newValue.FieldByName(attributeName)
	valueForAtt := reflect.ValueOf(attValue)
	field.Set(valueForAtt)

	// 从reflect.Value中获取新值
	newValInterface := newValue.Interface()
	// 将新值设置为指针
	*myUnknownTypeValue = newValInterface
}

英文:

Finally i find a way to do that. Follow the Go Playground and code snippet below:

GO Playground: Avoiding Hard Coded Type Assertion

<pre><code>
//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

}

</code></pre>

huangapple
  • 本文由 发表于 2015年6月2日 09:34:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/30585938.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定