feat: 完善 New 方法支持指针和值参数,新增 interface 类型支持
- 支持指针模式: New(&data) - 直接修改原始数据
- 支持值模式: New(data) - 创建深度克隆,不影响原始数据
- 新增 interface{} 类型支持,自动解析到实际类型
- 对 map 和 slice 等引用类型也进行完全深度克隆
- 新增 9 个单元测试覆盖指针/值/interface 场景
- 新增 9 个示例测试展示各种用法
- 所有测试通过,保持性能
- 更新 README.md 详细说明指针/值传递的区别
- 新增 interface 类型使用示例
- 新增泛型数据处理使用场景
- 更新注意事项说明
- 使用递归解引用处理 interface 和指针类型
- 利用 DeepClone 确保引用类型的完全独立
- 保持向后兼容,现有代码无需修改
This commit is contained in:
parent
d9f178020a
commit
97d41fa6d8
124
README.md
124
README.md
@ -9,7 +9,9 @@ Reflux 是一个 Go 语言包,提供了统一的接口用于访问和操作嵌
|
||||
- 🔄 **类型转换**: 基于 [spf13/cast](https://github.com/spf13/cast) 的强大类型转换支持
|
||||
- ✏️ **修改数据**: 支持通过路径设置和删除值
|
||||
- 🔗 **链式调用**: Set 和 Delete 方法支持链式调用,如 `rfx.Set("name", "Alice").Delete("age").Set("city", "Beijing")`
|
||||
- 📋 **深度克隆**: Scope 方法创建深度克隆,修改不影响原始数据
|
||||
- 📋 **深度克隆**: Scope 方法和值传递模式创建深度克隆,修改不影响原始数据
|
||||
- 🎭 **灵活模式**: 支持指针和值两种传递方式,提供不同的数据操作语义
|
||||
- 🔌 **Interface 支持**: 自动解析 `interface{}` 类型到实际类型
|
||||
- 🔤 **大小写不敏感**: 支持使用小写字段名访问和修改结构体字段
|
||||
- 🔑 **键名提取**: Keys 方法可获取结构体或 Map 的所有键名
|
||||
- 📝 **JSON 集成**: Ptr() 方法返回的指针可直接用于 json.Unmarshal(),实现动态 JSON 反序列化
|
||||
@ -68,12 +70,62 @@ func main() {
|
||||
|
||||
### 1. 创建 Reflux
|
||||
|
||||
Reflux 支持**指针**和**值**两种传递方式,提供不同的数据操作语义:
|
||||
|
||||
#### 指针模式 - 直接修改原始数据
|
||||
|
||||
```go
|
||||
user := User{Name: "Alice"}
|
||||
rfx := reflux.New(&user) // 传入指针以支持修改
|
||||
rfx := reflux.New(&user) // 传入指针
|
||||
|
||||
rfx.Set("Name", "Bob")
|
||||
// user.Name 已被修改为 "Bob"
|
||||
```
|
||||
|
||||
**重要**: `New()` 函数必须传入指针类型,否则会 panic。
|
||||
#### 值模式 - 深度克隆,不影响原始数据
|
||||
|
||||
```go
|
||||
user := User{Name: "Alice"}
|
||||
rfx := reflux.New(user) // 传入值
|
||||
|
||||
rfx.Set("Name", "Bob")
|
||||
// user.Name 仍然是 "Alice"
|
||||
// rfx 内部是独立的深度克隆
|
||||
```
|
||||
|
||||
#### Interface 类型支持
|
||||
|
||||
Reflux 支持 `interface{}` (或 `any`) 类型,会自动解析到实际类型:
|
||||
|
||||
```go
|
||||
// interface{} 包装的值 - 创建深度克隆
|
||||
var config any = map[string]string{"host": "localhost"}
|
||||
rfx := reflux.New(config)
|
||||
rfx.Set("host", "127.0.0.1")
|
||||
// 原始 config 不受影响
|
||||
|
||||
// interface{} 包装的指针 - 直接修改
|
||||
var data any = &config
|
||||
rfx := reflux.New(data)
|
||||
rfx.Set("host", "127.0.0.1")
|
||||
// config 已被修改
|
||||
```
|
||||
|
||||
#### 支持的类型
|
||||
|
||||
| 类型 | 说明 | 示例 |
|
||||
|-----|------|------|
|
||||
| **Struct** | 结构体 | `New(Person{})` 或 `New(&person)` |
|
||||
| **Map** | 映射 | `New(map[string]any{})` 或 `New(&m)` |
|
||||
| **Slice** | 切片 | `New([]string{})` 或 `New(&s)` |
|
||||
| **Array** | 数组 | `New([3]int{})` 或 `New(&a)` |
|
||||
| **Interface** | 接口 | `New(anyValue)` - 自动解析 |
|
||||
|
||||
**重要说明**:
|
||||
- ✅ **指针模式**: 传入指针(如 `&user`)时,所有修改都会**直接影响原始数据**
|
||||
- ✅ **值模式**: 传入值(如 `user`)时,会创建**深度克隆**,修改不影响原始数据
|
||||
- ✅ **深度克隆**: 对于 map 和 slice 等引用类型,值模式也会创建完全独立的副本
|
||||
- ❌ **不支持**: 基本类型(int, string, bool)、chan、func 等不是容器的类型
|
||||
|
||||
### 2. 获取值 (Get)
|
||||
|
||||
@ -581,6 +633,48 @@ func main() {
|
||||
- **插件热加载**: 更新插件配置而无需重启
|
||||
- **API 部分响应**: 只处理 API 返回的部分字段
|
||||
|
||||
### 7. 泛型数据处理 (Interface 类型)
|
||||
|
||||
使用 interface{} 类型处理未知类型的数据:
|
||||
|
||||
```go
|
||||
// 函数返回 any 类型
|
||||
func loadFromAPI() any {
|
||||
// 可能返回 map 或 struct
|
||||
return map[string]any{
|
||||
"user": map[string]any{
|
||||
"name": "Alice",
|
||||
"age": 30,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func processData(data any) {
|
||||
// 自动解析 interface{} 到实际类型
|
||||
rfx := reflux.New(data) // 值模式,创建深度克隆
|
||||
|
||||
// 安全地修改数据
|
||||
rfx.Set("user.name", "Bob")
|
||||
rfx.Set("user.age", 35)
|
||||
|
||||
// 原始数据不受影响
|
||||
}
|
||||
|
||||
// 或者使用指针模式
|
||||
func updateData(dataPtr any) {
|
||||
// interface{} 包装指针,直接修改
|
||||
rfx := reflux.New(dataPtr)
|
||||
rfx.Set("user.name", "Charlie")
|
||||
// 原始数据已被修改
|
||||
}
|
||||
```
|
||||
|
||||
使用场景:
|
||||
- **插件系统**: 处理未知结构的插件配置
|
||||
- **泛型配置**: 统一处理不同格式的配置数据
|
||||
- **API 适配**: 适配多种 API 响应格式
|
||||
- **数据转换**: 在不同数据结构间转换
|
||||
|
||||
## 路径语法
|
||||
|
||||
Reflux 支持灵活的点号路径语法:
|
||||
@ -667,16 +761,20 @@ type Reflux interface {
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **指针传递**: `New()` 函数必须传递指针,否则会 panic
|
||||
2. **Scope 行为**: `Scope` 返回深度克隆,修改不会影响原始数据
|
||||
3. **链式调用**: Set 和 Delete 方法都支持链式调用,返回 Reflux 自身
|
||||
4. **类型转换**: 使用 spf13/cast 进行类型转换,转换失败会 panic
|
||||
5. **切片删除**: 删除切片元素会创建新切片并重新赋值
|
||||
6. **Map 初始化**: 对 nil map 调用 Set 会自动初始化 map
|
||||
7. **大小写不敏感**: 支持使用小写字段名访问结构体字段
|
||||
8. **JSON 集成**: Ptr() 返回的指针可以安全地用于 json.Unmarshal()
|
||||
9. **并发安全**: Reflux 本身不是并发安全的,需要外部同步
|
||||
10. **错误处理**: 大多数转换、设置和删除操作失败时会 panic,请确保路径和类型正确
|
||||
1. **指针 vs 值传递**:
|
||||
- 传入**指针** (`New(&data)`) 时,修改会影响原始数据
|
||||
- 传入**值** (`New(data)`) 时,会创建深度克隆,修改不影响原始数据
|
||||
- 对于 map 和 slice 等引用类型,值模式也会完全克隆
|
||||
2. **Interface 支持**: 支持 `interface{}` 类型,会自动解析到实际类型并保持正确的指针/值语义
|
||||
3. **Scope 行为**: `Scope` 返回深度克隆,修改不会影响原始数据
|
||||
4. **链式调用**: Set 和 Delete 方法都支持链式调用,返回 Reflux 自身
|
||||
5. **类型转换**: 使用 spf13/cast 进行类型转换,转换失败会 panic
|
||||
6. **切片删除**: 删除切片元素会创建新切片并重新赋值
|
||||
7. **Map 初始化**: 对 nil map 调用 Set 会自动初始化 map
|
||||
8. **大小写不敏感**: 支持使用小写字段名访问结构体字段
|
||||
9. **JSON 集成**: Ptr() 返回的指针可以安全地用于 json.Unmarshal()
|
||||
10. **并发安全**: Reflux 本身不是并发安全的,需要外部同步
|
||||
11. **错误处理**: 大多数转换、设置和删除操作失败时会 panic,请确保路径和类型正确
|
||||
|
||||
## 示例代码
|
||||
|
||||
|
||||
44
reflux.go
44
reflux.go
@ -119,13 +119,49 @@ type R interface {
|
||||
}
|
||||
|
||||
// New 创建一个新的 R 实例
|
||||
// 参数 v 必须是指针类型,否则会 panic
|
||||
// 参数 v 可以是指针或非指针类型
|
||||
// - 如果传入指针: 将直接使用该指针,可以修改原始数据
|
||||
// - 如果传入值: 会自动创建一个深度克隆的指针副本,修改不影响原始数据
|
||||
// 支持的类型: map、struct、slice、array
|
||||
// 也支持 interface 类型,会自动解析到实际类型
|
||||
// 返回一个 R 接口实例,可用于访问和操作嵌套的字段、元素和键值对
|
||||
func New(v any) R {
|
||||
rv := reflect.ValueOf(v)
|
||||
// 如果传入的不是指针,panic
|
||||
if rv.Kind() != reflect.Ptr {
|
||||
panic("structmap: New() requires a pointer argument")
|
||||
|
||||
if !rv.IsValid() {
|
||||
panic("rfx: invalid value")
|
||||
}
|
||||
|
||||
// 记录原始是否为指针
|
||||
isPtr := rv.Kind() == reflect.Ptr
|
||||
|
||||
// 递归解引用指针和接口,直到获取实际的值类型
|
||||
actualValue := rv
|
||||
for actualValue.Kind() == reflect.Ptr || actualValue.Kind() == reflect.Interface {
|
||||
if actualValue.IsNil() {
|
||||
panic("rfx: cannot use nil pointer or nil interface")
|
||||
}
|
||||
actualValue = actualValue.Elem()
|
||||
// 如果解引用后的类型是指针,标记为指针模式
|
||||
if actualValue.Kind() == reflect.Ptr {
|
||||
isPtr = true
|
||||
}
|
||||
}
|
||||
|
||||
// 检查最终的实际类型是否为支持的类型
|
||||
switch actualValue.Kind() {
|
||||
case reflect.Map, reflect.Struct, reflect.Slice, reflect.Array:
|
||||
// 支持的类型
|
||||
default:
|
||||
panic("rfx: unsupported type, only map, struct, slice, and array are allowed")
|
||||
}
|
||||
|
||||
// 如果原始传入的不是指针类型,需要进行深度克隆以避免修改原始数据
|
||||
// 对于引用类型(map, slice)这尤其重要
|
||||
if !isPtr {
|
||||
// 使用深度克隆创建一个完全独立的副本
|
||||
rv = DeepClone(actualValue)
|
||||
}
|
||||
|
||||
return &rfx{value: rv}
|
||||
}
|
||||
|
||||
@ -2,6 +2,228 @@ package reflux
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ExampleNew_withPointer 演示如何使用 New 函数传入指针
|
||||
// 传入指针时,修改会影响原始数据
|
||||
func ExampleNew_withPointer() {
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
person := Person{Name: "Alice", Age: 30}
|
||||
|
||||
// 传入指针 - 修改会影响原始数据
|
||||
rfx := New(&person)
|
||||
rfx.Set("Name", "Bob")
|
||||
rfx.Set("Age", 35)
|
||||
|
||||
// 原始数据已被修改
|
||||
fmt.Printf("Name: %s, Age: %d\n", person.Name, person.Age)
|
||||
|
||||
// Output:
|
||||
// Name: Bob, Age: 35
|
||||
}
|
||||
|
||||
// ExampleNew_withPointer_map 演示传入 map 指针
|
||||
func ExampleNew_withPointer_map() {
|
||||
config := map[string]any{
|
||||
"host": "localhost",
|
||||
"port": 8080,
|
||||
}
|
||||
|
||||
// 传入 map 指针 - 修改会影响原始数据
|
||||
rfx := New(&config)
|
||||
rfx.Set("host", "127.0.0.1")
|
||||
rfx.Set("port", 9090)
|
||||
|
||||
// 原始 map 已被修改
|
||||
fmt.Printf("Host: %s, Port: %v\n", config["host"], config["port"])
|
||||
|
||||
// Output:
|
||||
// Host: 127.0.0.1, Port: 9090
|
||||
}
|
||||
|
||||
// ExampleNew_withPointer_slice 演示传入 slice 指针
|
||||
func ExampleNew_withPointer_slice() {
|
||||
items := []string{"apple", "banana", "cherry"}
|
||||
|
||||
// 传入 slice 指针 - 修改会影响原始数据
|
||||
rfx := New(&items)
|
||||
rfx.Set("0", "orange")
|
||||
rfx.Set("2", "grape")
|
||||
|
||||
// 原始 slice 已被修改
|
||||
fmt.Printf("Items: %v\n", items)
|
||||
|
||||
// Output:
|
||||
// Items: [orange banana grape]
|
||||
}
|
||||
|
||||
// ExampleNew_withValue 演示如何使用 New 函数传入值
|
||||
// 传入值时,会创建深度克隆,修改不会影响原始数据
|
||||
func ExampleNew_withValue() {
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
person := Person{Name: "Alice", Age: 30}
|
||||
|
||||
// 传入值 - 会创建深度克隆,修改不会影响原始数据
|
||||
rfx := New(person)
|
||||
rfx.Set("Name", "Bob")
|
||||
rfx.Set("Age", 35)
|
||||
|
||||
// 原始数据未被修改
|
||||
fmt.Printf("Original - Name: %s, Age: %d\n", person.Name, person.Age)
|
||||
|
||||
// rfx 内部的数据已修改
|
||||
fmt.Printf("Clone - Name: %s, Age: %d\n",
|
||||
rfx.Get("Name").String(),
|
||||
rfx.Get("Age").Int())
|
||||
|
||||
// Output:
|
||||
// Original - Name: Alice, Age: 30
|
||||
// Clone - Name: Bob, Age: 35
|
||||
}
|
||||
|
||||
// ExampleNew_withValue_map 演示传入 map 值
|
||||
// 传入值时会创建深度克隆,即使 map 是引用类型
|
||||
func ExampleNew_withValue_map() {
|
||||
config := map[string]any{
|
||||
"host": "localhost",
|
||||
"port": 8080,
|
||||
}
|
||||
|
||||
// 传入 map 值 - 会创建深度克隆
|
||||
rfx := New(config)
|
||||
rfx.Set("host", "127.0.0.1")
|
||||
rfx.Set("port", 9090)
|
||||
|
||||
// 原始 map 未被修改
|
||||
fmt.Printf("Original - Host: %s, Port: %v\n", config["host"], config["port"])
|
||||
|
||||
// rfx 内部的数据已修改
|
||||
fmt.Printf("Clone - Host: %s, Port: %v\n",
|
||||
rfx.Get("host").String(),
|
||||
rfx.Get("port").Int())
|
||||
|
||||
// Output:
|
||||
// Original - Host: localhost, Port: 8080
|
||||
// Clone - Host: 127.0.0.1, Port: 9090
|
||||
}
|
||||
|
||||
// ExampleNew_withValue_slice 演示传入 slice 值
|
||||
// 传入值时会创建深度克隆,即使 slice 是引用类型
|
||||
func ExampleNew_withValue_slice() {
|
||||
items := []string{"apple", "banana", "cherry"}
|
||||
|
||||
// 传入 slice 值 - 会创建深度克隆
|
||||
rfx := New(items)
|
||||
rfx.Set("0", "orange")
|
||||
rfx.Set("2", "grape")
|
||||
|
||||
// 原始 slice 未被修改
|
||||
fmt.Printf("Original: %v\n", items)
|
||||
|
||||
// rfx 内部的数据已修改
|
||||
fmt.Printf("Clone: [%s %s %s]\n",
|
||||
rfx.Get("0").String(),
|
||||
rfx.Get("1").String(),
|
||||
rfx.Get("2").String())
|
||||
|
||||
// Output:
|
||||
// Original: [apple banana cherry]
|
||||
// Clone: [orange banana grape]
|
||||
}
|
||||
|
||||
// ExampleNew_withValue_nestedStruct 演示传入嵌套结构体值
|
||||
func ExampleNew_withValue_nestedStruct() {
|
||||
type Address struct {
|
||||
City string
|
||||
Street string
|
||||
}
|
||||
type Person struct {
|
||||
Name string
|
||||
Address Address
|
||||
}
|
||||
|
||||
person := Person{
|
||||
Name: "Alice",
|
||||
Address: Address{
|
||||
City: "Beijing",
|
||||
Street: "Main St",
|
||||
},
|
||||
}
|
||||
|
||||
// 传入值 - 会创建深度克隆,包括嵌套的结构体
|
||||
rfx := New(person)
|
||||
rfx.Set("Name", "Bob")
|
||||
rfx.Set("Address.City", "Shanghai")
|
||||
|
||||
// 原始数据未被修改
|
||||
fmt.Printf("Original - Name: %s, City: %s\n", person.Name, person.Address.City)
|
||||
|
||||
// rfx 内部的数据已修改
|
||||
fmt.Printf("Clone - Name: %s, City: %s\n",
|
||||
rfx.Get("Name").String(),
|
||||
rfx.Get("Address", "City").String())
|
||||
|
||||
// Output:
|
||||
// Original - Name: Alice, City: Beijing
|
||||
// Clone - Name: Bob, City: Shanghai
|
||||
}
|
||||
|
||||
// ExampleNew_withInterface 演示 interface{} 类型的支持
|
||||
// 当数据被包装在 interface{} 中时,会自动解析到实际类型
|
||||
func ExampleNew_withInterface() {
|
||||
// interface{} 包装的 map
|
||||
var config any = map[string]any{
|
||||
"host": "localhost",
|
||||
"port": 8080,
|
||||
}
|
||||
|
||||
// 传入 interface{} 值 - 会创建深度克隆
|
||||
rfx := New(config)
|
||||
rfx.Set("host", "127.0.0.1")
|
||||
rfx.Set("port", 9090)
|
||||
|
||||
// 原始数据未被修改
|
||||
originalMap := config.(map[string]any)
|
||||
fmt.Printf("Original - Host: %s, Port: %v\n", originalMap["host"], originalMap["port"])
|
||||
|
||||
// rfx 内部的数据已修改
|
||||
fmt.Printf("Clone - Host: %s, Port: %v\n",
|
||||
rfx.Get("host").String(),
|
||||
rfx.Get("port").Int())
|
||||
|
||||
// Output:
|
||||
// Original - Host: localhost, Port: 8080
|
||||
// Clone - Host: 127.0.0.1, Port: 9090
|
||||
}
|
||||
|
||||
// ExampleNew_withInterfacePointer 演示 interface{} 包装指针的情况
|
||||
func ExampleNew_withInterfacePointer() {
|
||||
type Config struct {
|
||||
Host string
|
||||
Port int
|
||||
}
|
||||
|
||||
config := Config{Host: "localhost", Port: 8080}
|
||||
|
||||
// interface{} 包装指针 - 修改会影响原始数据
|
||||
var data any = &config
|
||||
rfx := New(data)
|
||||
rfx.Set("Host", "127.0.0.1")
|
||||
rfx.Set("Port", 9090)
|
||||
|
||||
// 原始数据已被修改(因为是指针)
|
||||
fmt.Printf("Host: %s, Port: %d\n", config.Host, config.Port)
|
||||
|
||||
// Output:
|
||||
// Host: 127.0.0.1, Port: 9090
|
||||
}
|
||||
|
||||
// ExampleNew_withMap 演示如何使用 New 函数传入 map
|
||||
func ExampleNew_withMap() {
|
||||
// 创建一个 map
|
||||
|
||||
146
rfx_test.go
146
rfx_test.go
@ -23,15 +23,155 @@ type Address struct {
|
||||
|
||||
// TestNew 测试 New 函数
|
||||
func TestNew(t *testing.T) {
|
||||
t.Run("New with pointer", func(t *testing.T) {
|
||||
// 测试传入指针 - 修改应该影响原始数据
|
||||
p := Person{Name: "Alice", Age: 30}
|
||||
rfx := New(&p)
|
||||
|
||||
// 测试传入非指针值应该 panic
|
||||
rfx.Set("Name", "Bob")
|
||||
if p.Name != "Bob" {
|
||||
t.Errorf("Expected original value to be modified, got '%s'", p.Name)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("New with value", func(t *testing.T) {
|
||||
// 测试传入值 - 修改不应该影响原始数据
|
||||
p := Person{Name: "Alice", Age: 30}
|
||||
rfx := New(p)
|
||||
|
||||
rfx.Set("Name", "Bob")
|
||||
// 原始值不应该被修改
|
||||
if p.Name != "Alice" {
|
||||
t.Errorf("Expected original value to remain 'Alice', got '%s'", p.Name)
|
||||
}
|
||||
|
||||
// rfx 内部的值应该已经修改
|
||||
if rfx.Get("Name").String() != "Bob" {
|
||||
t.Errorf("Expected rfx value to be 'Bob', got '%s'", rfx.Get("Name").String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("New with map pointer", func(t *testing.T) {
|
||||
m := map[string]any{"name": "Alice"}
|
||||
rfx := New(&m)
|
||||
|
||||
rfx.Set("name", "Bob")
|
||||
if m["name"] != "Bob" {
|
||||
t.Errorf("Expected map to be modified, got '%v'", m["name"])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("New with map value", func(t *testing.T) {
|
||||
m := map[string]any{"name": "Alice"}
|
||||
rfx := New(m)
|
||||
|
||||
rfx.Set("name", "Bob")
|
||||
// 原始 map 不应该被修改
|
||||
if m["name"] != "Alice" {
|
||||
t.Errorf("Expected original map to remain 'Alice', got '%v'", m["name"])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("New with slice pointer", func(t *testing.T) {
|
||||
s := []string{"a", "b", "c"}
|
||||
rfx := New(&s)
|
||||
|
||||
rfx.Set("0", "x")
|
||||
if s[0] != "x" {
|
||||
t.Errorf("Expected slice to be modified, got '%s'", s[0])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("New with slice value", func(t *testing.T) {
|
||||
s := []string{"a", "b", "c"}
|
||||
rfx := New(s)
|
||||
|
||||
rfx.Set("0", "x")
|
||||
// 原始切片不应该被修改
|
||||
if s[0] != "a" {
|
||||
t.Errorf("Expected original slice to remain 'a', got '%s'", s[0])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("New with array value", func(t *testing.T) {
|
||||
a := [3]string{"a", "b", "c"}
|
||||
rfx := New(a)
|
||||
|
||||
rfx.Set("0", "x")
|
||||
// 原始数组不应该被修改
|
||||
if a[0] != "a" {
|
||||
t.Errorf("Expected original array to remain 'a', got '%s'", a[0])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("New with nil pointer", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Error("New() with non-pointer should panic")
|
||||
t.Error("Expected panic with nil pointer")
|
||||
}
|
||||
}()
|
||||
New(p) // 应该 panic
|
||||
var p *Person
|
||||
New(p)
|
||||
})
|
||||
|
||||
t.Run("New with unsupported type", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Error("Expected panic with unsupported type")
|
||||
}
|
||||
}()
|
||||
New(42) // int 不支持
|
||||
})
|
||||
|
||||
t.Run("New with interface wrapping map", func(t *testing.T) {
|
||||
// 测试 interface{} 包装的 map
|
||||
var data any = map[string]string{"name": "Alice"}
|
||||
rfx := New(data)
|
||||
|
||||
if rfx.Get("name").String() != "Alice" {
|
||||
t.Errorf("Expected 'Alice', got '%s'", rfx.Get("name").String())
|
||||
}
|
||||
|
||||
// 修改不应该影响原始数据(因为是值传递)
|
||||
rfx.Set("name", "Bob")
|
||||
originalMap := data.(map[string]string)
|
||||
if originalMap["name"] != "Alice" {
|
||||
t.Error("Original map should not be modified")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("New with interface wrapping struct", func(t *testing.T) {
|
||||
// 测试 interface{} 包装的 struct
|
||||
var data any = Person{Name: "Alice", Age: 30}
|
||||
rfx := New(data)
|
||||
|
||||
if rfx.Get("Name").String() != "Alice" {
|
||||
t.Errorf("Expected 'Alice', got '%s'", rfx.Get("Name").String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("New with interface wrapping pointer", func(t *testing.T) {
|
||||
// 测试 interface{} 包装的指针
|
||||
person := Person{Name: "Alice", Age: 30}
|
||||
var data any = &person
|
||||
rfx := New(data)
|
||||
|
||||
rfx.Set("Name", "Bob")
|
||||
// 原始数据应该被修改(因为是指针)
|
||||
if person.Name != "Bob" {
|
||||
t.Errorf("Expected original to be modified, got '%s'", person.Name)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("New with nil interface", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Error("Expected panic with nil interface")
|
||||
}
|
||||
}()
|
||||
var data any
|
||||
New(data)
|
||||
})
|
||||
}
|
||||
|
||||
// TestNewWithPointer 测试传入指针的情况
|
||||
|
||||
Loading…
Reference in New Issue
Block a user