Background
我正在构建一个包装器包,它结合了已定义的命令,并允许它们在cli或交互式shell上下文中执行 . 命令在结构中定义,如下所示(显示相关字段):
type Handler func(c *Command) error
type Command struct {
RequestHandler Handler
ResponseHandler Handler
Request interface{} //pointer to struct
Response interface{} //pointer to struct
}
底层的Request / Response对象总是指向结构的指针,所以我非常大量地使用反射来递归迭代底层的结构字段,根据调用上下文(shell / cli)将它们的字段显示为标志或提示 . 然后,我根据用户输入使用反射填充请求对象 .
到现在为止还挺好...
Issue
当我在交互式shell上下文中时,可以调用一个命令,并填充请求结构及其值 - 一切都很好 . 但是,如果再次调用该命令,则仍会填充请求(并且响应可能也是如此),因此我希望在命令上实现'reset'方法,当从shell调用命令时可以在内部调用该方法 .
现在我说我可以保证请求和响应都是结构的指针 . 但是当我所拥有的只是一个接口并且没有基础类型的高级知识((和内部字段可能是结构值)时,如何将它们设置回各自初始化的nil值 .
Possible Approaches
-
在命令注册时(在填充之前)添加底层请求/响应对象的第二个副本,然后在重置时复制它
-
只是创建一个通用的重置结构函数,它作用于底层结构指针的任何接口
-
某种方法我不知道这对我来说是什么
我已经尝试了两种方法,我也在努力实施
approach 1:
type Command struct {
RequestHandler Handler
ResponseHandler Handler
zeroRequest interface{}
zeroResponse interface{}
Request interface{} //pointer to struct
Response interface{} //pointer to struct
}
func (c *Command) register() error {
// validate is pointer
reqPtrVal := reflect.ValueOf(c.Request)
if reqPtrVal.Kind() != reflect.Ptr {
return ErrStructPtrExpected
}
// reflect the underlying value and validate is struct
reqVal := reflect.Indirect(reqPtrVal)
if reqVal.Kind() != reflect.Struct {
return ErrStructPtrExpected
}
c.zeroRequest = reqVal.Interface()
// other logic...
}
func (c *Command) handleShell(sc *ishell.Context) {
// reset request / response objects
// reflect the underlying zero value and validate is struct
reqVal := reflect.ValueOf(c.zeroRequest)
if reqVal.Kind() != reflect.Struct {
c.Commander.HandleError(ErrStructPtrExpected)
}
newRequest := reflect.New(reqVal.Type())
// unfortunately below doesn't work as newRequest.CanAddr() == false
c.zeroRequest = newRequest.Addr().Interface()
// other logic
}
approach 2: 这感觉有点干净(没有零值拷贝/更少的代码),但我甚至无法编译这种方法:
func (c *Command) resetStruct(structPtr interface{}) error {
// validate is pointer
ptrVal := reflect.ValueOf(structPtr)
if ptrVal.Kind() != reflect.Ptr {
return ErrStructPtrExpected
}
// reflect the underlying value and validate is struct
val := reflect.Indirect(ptrVal)
if val.Kind() != reflect.Struct {
return ErrStructPtrExpected
}
// create a new val from the underlying type and reassign the pointer
newVal := reflect.New(val.Type())
ptrVal.SetPointer(newVal.Addr())
return nil
}
Summary
最终目标是通过反射将这个未知类型的结构归零,并将指针重新分配回请求接口 . 接近的优先顺序是3,2,1 - 但任何有效的方法都只是花花公子 .
似乎我可以解决这个问题,如果我可以从reflect.Type创建一个具体的类型,但也许这里有另一种方法
1 回答
您可以使用
reflect.Zero
或reflect.New
,请查看下面的代码段以获得清晰度这是你如何使用它
玩:https://play.golang.com/p/8CJHHBL6c2p