面试题答案
一键面试1. 设计场景
假设我们有一个简单的计算器程序,支持多种运算(加、减、乘、除)。我们希望通过反射动态调用不同的运算函数,并且根据用户输入动态确定函数参数类型和返回值类型。
2. 代码实现
package main
import (
"fmt"
"reflect"
)
// 定义运算函数
func add(a, b int) int {
return a + b
}
func subtract(a, b int) int {
return a - b
}
func main() {
// 定义函数映射
functionMap := map[string]interface{}{
"add": add,
"subtract": subtract,
}
// 用户输入的函数名和参数
functionName := "add"
args := []interface{}{10, 20}
// 获取函数
functionValue := reflect.ValueOf(functionMap[functionName])
if functionValue.Kind() != reflect.Func {
fmt.Println("Invalid function name")
return
}
// 构建参数列表
var argValues []reflect.Value
for _, arg := range args {
argValues = append(argValues, reflect.ValueOf(arg))
}
// 调用函数
results := functionValue.Call(argValues)
// 处理返回值
if len(results) > 0 {
resultValue := results[0]
fmt.Printf("Result: %v\n", resultValue.Interface())
}
}
3. 利用反射第三定律进行修改
假设我们希望通过反射修改函数内部的一个变量(虽然在实际计算器场景中可能不太常见,但为了展示反射第三定律)。
package main
import (
"fmt"
"reflect"
)
// 定义一个带有可修改变量的函数
func modifyValue(a *int) {
*a = *a + 1
}
func main() {
num := 10
valueOfNum := reflect.ValueOf(&num)
if valueOfNum.Kind() != reflect.Ptr {
fmt.Println("Expected pointer")
return
}
// 确保值是可设置的
if valueOfNum.Elem().CanSet() {
funcValue := reflect.ValueOf(modifyValue)
argValue := reflect.ValueOf(&num)
funcValue.Call([]reflect.Value{argValue})
fmt.Printf("Modified value: %d\n", num)
}
}
4. 类型断言操作
在上述代码中,我们在获取函数值时,通过 Kind()
方法进行了类型断言,确保获取的是函数类型:
functionValue := reflect.ValueOf(functionMap[functionName])
if functionValue.Kind() != reflect.Func {
fmt.Println("Invalid function name")
return
}
5. 并发问题及解决策略
并发问题:
- 多个协程同时调用反射函数,可能导致数据竞争,因为反射操作本身不是线程安全的。
- 对共享资源(如函数映射)的并发读写可能导致数据不一致。
解决策略:
- 使用互斥锁(
sync.Mutex
)保护共享资源。例如,在访问functionMap
时加锁:
var mu sync.Mutex
functionMap := map[string]interface{}{
"add": add,
"subtract": subtract,
}
// 获取函数时加锁
mu.Lock()
functionValue := reflect.ValueOf(functionMap[functionName])
mu.Unlock()
- 使用
sync.RWMutex
进行读写锁控制,适用于读多写少的场景,提高并发性能。 - 尽量避免在反射操作中共享可变状态,将数据复制到每个协程的本地空间进行处理,减少数据竞争。