feat: 支持反射设置指针类型字段
- 如果目标字段是指针类型,它会尝试直接赋值(如果类型兼容),或创建一个新指针并递归设置其指向的值。 - `rfx_example_test.go` 中新增了测试用例,以验证对 `*string` 等指针字段的设置功能。
This commit is contained in:
parent
f9e7f8e781
commit
cbe079ddcd
21
rfx.go
21
rfx.go
@ -257,6 +257,27 @@ func (r *rfx) setValue(field reflect.Value, v any) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// 处理指针类型
|
||||
if field.Type().Kind() == reflect.Ptr {
|
||||
// 如果传入的值已经是指针类型,尝试直接赋值
|
||||
if val.Type().AssignableTo(field.Type()) {
|
||||
field.Set(val)
|
||||
return true
|
||||
}
|
||||
|
||||
// 如果传入的值不是指针,创建新指针并设置值
|
||||
elemType := field.Type().Elem()
|
||||
newPtr := reflect.New(elemType)
|
||||
|
||||
// 递归设置指针指向的值
|
||||
if !r.setValue(newPtr.Elem(), v) {
|
||||
return false
|
||||
}
|
||||
|
||||
field.Set(newPtr)
|
||||
return true
|
||||
}
|
||||
|
||||
// 优先使用 cast 进行智能类型转换
|
||||
// 这样可以处理 string <-> number, number <-> bool 等常见转换
|
||||
targetType := field.Type()
|
||||
|
||||
@ -6,22 +6,27 @@ import "fmt"
|
||||
// 传入指针时,修改会影响原始数据
|
||||
func ExampleNew_withPointer() {
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
Name string
|
||||
Age int
|
||||
Email *string
|
||||
}
|
||||
|
||||
person := Person{Name: "Alice", Age: 30}
|
||||
email := "alice@example.com"
|
||||
person := Person{Name: "Alice", Age: 30, Email: &email}
|
||||
|
||||
// 传入指针 - 修改会影响原始数据
|
||||
rfx := New(&person)
|
||||
rfx.Set("Name", "Bob")
|
||||
rfx.Set("Age", 35)
|
||||
|
||||
// 原始数据已被修改
|
||||
fmt.Printf("Name: %s, Age: %d\n", person.Name, person.Age)
|
||||
// 对于指针字段,Set 会设置指针指向的值
|
||||
rfx.Set("Email", "bob@example.com")
|
||||
|
||||
// 原始数据已被修改(包括指针字段指向的值)
|
||||
fmt.Printf("Name: %s, Age: %d, Email: %s\n", person.Name, person.Age, *person.Email)
|
||||
|
||||
// Output:
|
||||
// Name: Bob, Age: 35
|
||||
// Name: Bob, Age: 35, Email: bob@example.com
|
||||
}
|
||||
|
||||
// ExampleNew_withPointer_map 演示传入 map 指针
|
||||
@ -63,21 +68,27 @@ func ExampleNew_withPointer_slice() {
|
||||
// 传入值时,会创建深度克隆,修改不会影响原始数据
|
||||
func ExampleNew_withValue() {
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
Name string
|
||||
Age int
|
||||
Email *string
|
||||
}
|
||||
|
||||
person := Person{Name: "Alice", Age: 30}
|
||||
email := "alice@example.com"
|
||||
person := Person{Name: "Alice", Age: 30, Email: &email}
|
||||
|
||||
// 传入值 - 会创建深度克隆,修改不会影响原始数据
|
||||
// 传入值 - 会创建深度克隆,修改不会影响原始数据(包括指针字段)
|
||||
rfx := New(person)
|
||||
fmt.Printf("After New: %+v\n", rfx.Any())
|
||||
|
||||
// 显示克隆后的数据(指针字段会显示实际值)
|
||||
clonedPerson := rfx.Any().(Person)
|
||||
fmt.Printf("After New - Name: %s, Age: %d, Email: %s\n",
|
||||
clonedPerson.Name, clonedPerson.Age, *clonedPerson.Email)
|
||||
|
||||
rfx.Set("Name", "Bob")
|
||||
rfx.Set("Age", 35)
|
||||
|
||||
// 原始数据未被修改
|
||||
fmt.Printf("Original - Name: %s, Age: %d\n", person.Name, person.Age)
|
||||
fmt.Printf("Original - Name: %s, Age: %d, Email: %s\n", person.Name, person.Age, *person.Email)
|
||||
|
||||
// rfx 内部的数据已修改
|
||||
fmt.Printf("Clone - Name: %s, Age: %d\n",
|
||||
@ -85,38 +96,54 @@ func ExampleNew_withValue() {
|
||||
rfx.Get("Age").Int())
|
||||
|
||||
// Output:
|
||||
// After New: {Name:Alice Age:30}
|
||||
// Original - Name: Alice, Age: 30
|
||||
// After New - Name: Alice, Age: 30, Email: alice@example.com
|
||||
// Original - Name: Alice, Age: 30, Email: alice@example.com
|
||||
// Clone - Name: Bob, Age: 35
|
||||
}
|
||||
|
||||
// ExampleNew_withValue_map 演示传入 map 值
|
||||
// 传入值时会创建深度克隆,即使 map 是引用类型
|
||||
func ExampleNew_withValue_map() {
|
||||
host := "localhost"
|
||||
port := 8080
|
||||
config := map[string]any{
|
||||
"host": "localhost",
|
||||
"port": 8080,
|
||||
"host": &host,
|
||||
"port": &port,
|
||||
"enabled": true,
|
||||
}
|
||||
|
||||
// 传入 map 值 - 会创建深度克隆
|
||||
// 传入 map 值 - 会创建深度克隆(包括指针值)
|
||||
rfx := New(config)
|
||||
fmt.Printf("After New: %v\n", rfx.Any())
|
||||
|
||||
rfx.Set("host", "127.0.0.1")
|
||||
rfx.Set("port", 9090)
|
||||
// 显示克隆后的数据(指针值会显示实际内容)
|
||||
clonedConfig := rfx.Any().(map[string]any)
|
||||
fmt.Printf("After New - Host: %s, Port: %d, Enabled: %t\n",
|
||||
*clonedConfig["host"].(*string),
|
||||
*clonedConfig["port"].(*int),
|
||||
clonedConfig["enabled"].(bool))
|
||||
|
||||
// 修改克隆的数据
|
||||
newHost := "127.0.0.1"
|
||||
newPort := 9090
|
||||
clonedConfig["host"] = &newHost
|
||||
clonedConfig["port"] = &newPort
|
||||
|
||||
// 原始 map 未被修改
|
||||
fmt.Printf("Original - Host: %s, Port: %v\n", config["host"], config["port"])
|
||||
fmt.Printf("Original - Host: %s, Port: %d, Enabled: %t\n",
|
||||
*config["host"].(*string),
|
||||
*config["port"].(*int),
|
||||
config["enabled"].(bool))
|
||||
|
||||
// rfx 内部的数据已修改
|
||||
fmt.Printf("Clone - Host: %s, Port: %v\n",
|
||||
rfx.Get("host").String(),
|
||||
rfx.Get("port").Int())
|
||||
// 克隆的数据已修改
|
||||
fmt.Printf("Clone - Host: %s, Port: %d, Enabled: %t\n",
|
||||
*clonedConfig["host"].(*string),
|
||||
*clonedConfig["port"].(*int),
|
||||
clonedConfig["enabled"].(bool))
|
||||
|
||||
// Output:
|
||||
// After New: map[host:localhost port:8080]
|
||||
// Original - Host: localhost, Port: 8080
|
||||
// Clone - Host: 127.0.0.1, Port: 9090
|
||||
// After New - Host: localhost, Port: 8080, Enabled: true
|
||||
// Original - Host: localhost, Port: 8080, Enabled: true
|
||||
// Clone - Host: 127.0.0.1, Port: 9090, Enabled: true
|
||||
}
|
||||
|
||||
// ExampleNew_withValue_slice 演示传入 slice 值
|
||||
|
||||
67
util.go
67
util.go
@ -95,26 +95,69 @@ func cloneSequence(dst, src reflect.Value) {
|
||||
}
|
||||
|
||||
// cloneElement 克隆单个元素,处理 interface 包装的情况
|
||||
// 对于复杂类型(struct, map, slice, array)进行深度克隆
|
||||
// 对于复杂类型(struct, map, slice, array)和指针类型进行深度克隆
|
||||
// 对于基本类型,直接返回原始值
|
||||
func cloneElement(elem reflect.Value) reflect.Value {
|
||||
// 解引用以获取实际值
|
||||
actualElem := elem
|
||||
for actualElem.Kind() == reflect.Interface || actualElem.Kind() == reflect.Ptr {
|
||||
if actualElem.IsNil() {
|
||||
return elem
|
||||
// 首先检查 elem 自身是否是 interface
|
||||
if elem.Kind() == reflect.Interface && !elem.IsNil() {
|
||||
// 获取 interface 包装的实际值
|
||||
wrapped := elem.Elem()
|
||||
|
||||
// 如果 interface 包装的是指针,需要深度克隆指针
|
||||
if wrapped.Kind() == reflect.Ptr && !wrapped.IsNil() {
|
||||
// 克隆指针指向的值
|
||||
pointedValue := wrapped.Elem()
|
||||
if needsDeepClone(pointedValue.Kind()) {
|
||||
// 复杂类型:递归克隆
|
||||
clonedValue := reflect.New(pointedValue.Type()).Elem()
|
||||
CloneValue(clonedValue, pointedValue)
|
||||
clonedPtr := reflect.New(pointedValue.Type())
|
||||
clonedPtr.Elem().Set(clonedValue)
|
||||
return clonedPtr
|
||||
} else {
|
||||
// 基本类型:创建新指针
|
||||
clonedPtr := reflect.New(pointedValue.Type())
|
||||
clonedPtr.Elem().Set(pointedValue)
|
||||
return clonedPtr
|
||||
}
|
||||
}
|
||||
actualElem = actualElem.Elem()
|
||||
|
||||
// interface 包装的是复杂类型
|
||||
if needsDeepClone(wrapped.Kind()) {
|
||||
clonedElem := reflect.New(wrapped.Type()).Elem()
|
||||
CloneValue(clonedElem, wrapped)
|
||||
return clonedElem
|
||||
}
|
||||
|
||||
// interface 包装的是基本类型,直接返回
|
||||
return elem
|
||||
}
|
||||
|
||||
// 如果元素需要递归克隆(struct, map, slice, array)
|
||||
if actualElem.IsValid() && needsDeepClone(actualElem.Kind()) {
|
||||
clonedElem := reflect.New(actualElem.Type()).Elem()
|
||||
CloneValue(clonedElem, actualElem)
|
||||
// 处理直接的指针类型
|
||||
if elem.Kind() == reflect.Ptr && !elem.IsNil() {
|
||||
pointedValue := elem.Elem()
|
||||
if needsDeepClone(pointedValue.Kind()) {
|
||||
clonedValue := reflect.New(pointedValue.Type()).Elem()
|
||||
CloneValue(clonedValue, pointedValue)
|
||||
clonedPtr := reflect.New(pointedValue.Type())
|
||||
clonedPtr.Elem().Set(clonedValue)
|
||||
return clonedPtr
|
||||
} else {
|
||||
// 基本类型的指针,创建新指针
|
||||
clonedPtr := reflect.New(pointedValue.Type())
|
||||
clonedPtr.Elem().Set(pointedValue)
|
||||
return clonedPtr
|
||||
}
|
||||
}
|
||||
|
||||
// 处理复杂类型
|
||||
if needsDeepClone(elem.Kind()) {
|
||||
clonedElem := reflect.New(elem.Type()).Elem()
|
||||
CloneValue(clonedElem, elem)
|
||||
return clonedElem
|
||||
}
|
||||
|
||||
// 对于基本类型,直接返回原始值
|
||||
// 基本类型,直接返回
|
||||
return elem
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user