如何创建一个可寻址的getter函数,用于从接口进行类型转换。

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

How to make an addressable getter that casts from an interface

问题

我有一个名为Context的概念,它是一个可以容纳任何结构的映射。基本上,我想创建一个通用的getter函数,类似于JSON解码的方式,可以“填充”目标接口。

以下是我希望它工作的示例:

type Context map[string]interface{}

// 将保存在上下文中的随机结构体
type Step struct { 
	Name string
}

func main() {
	stepA := &Step{Name: "Cool Name"}

	c := Context{}
	c["stepA"] = stepA

	var stepB *Step
	err := c.Get("stepA", &stepB)
	if err != nil {
		panic(err)
	}

	fmt.Println(stepB.Name) // Cool Name
	stepB.Name = "CoolName2"

	fmt.Println(stepA.Name) // 我希望输出: CoolName2
}

func (c Context) Get(stepId string, dest interface{}) error {
	context, ok := c[stepId]
	if !ok {
		return nil
	}

	destinationValue := reflect.ValueOf(dest)
	contextValue := reflect.ValueOf(context)

	destinationValue.Elem().Set(contextValue.Elem()) // 这里会报错
	return nil
}

我倾向于使用反射(reflect),但也许我不需要它?所以我对其他建议持开放态度(除了泛型,因为那会使其他事情变得复杂)。我在上述代码中遇到了以下错误:

panic: reflect: reflect.Value.Set using unaddressable value

你可以在这里进行测试。

英文:

I have the concept of Context which is a map that can hold any structure. Basically, I want to create a generic getter that adddressably 'populates' the destination interface (similarly to how json decoding works).

Here's an example of how I want this to work:

type Context map[string]interface{}

// Random struct that will be saved in the context
type Step struct { 
	Name string
}

func main() {
	stepA := &Step{Name: "Cool Name"}

	c := Context{}
	c["stepA"] = stepA

	var stepB *Step
	err := c.Get("stepA", stepB)
	if err != nil {
		panic(err)
	}

	fmt.Println(stepB.Name) // Cool Name
	stepB.Name = "CoolName2"

	fmt.Println(stepA.Name) // I want to say: CoolName2
}

func (c Context) Get(stepId string, dest interface{}) error {
	context, ok := c[stepId]
	if !ok {
		return nil
	}

	destinationValue := reflect.ValueOf(dest)
	contextValue := reflect.ValueOf(context)

	destinationValue.Set(contextValue) // Errors here
	return nil
}

I leaned towards using reflect, but maybe I don't need it? - so opened to other suggestions (except for generics as that complicates other matters) I'm getting the following error with the above:

panic: reflect: reflect.Value.Set using unaddressable value

You can test it here.

答案1

得分: 1

传递给Get方法的参数必须是指针类型,其元素类型与上下文映射中的类型相同。因此,如果上下文映射中的值的类型是*Step,那么参数的类型必须是**Step。此外,传入的参数不能为nil,它可以是指向nil的指针,但它本身不能是nil

所以在你的情况下,你应该这样做:

var stepB *Step
err := c.Get("stepA", &stepB) // 传递指针的指针
if err != nil {
    panic(err)
}

并且稍作修改的Get方法如下:

func (c Context) Get(stepId string, dest interface{}) error {
    context, ok := c[stepId]
    if !ok {
        return nil
    }

    dv := reflect.ValueOf(dest)
    if dv.Kind() != reflect.Ptr || dv.IsNil() {
        return errors.New("dest must be non-nil pointer")
    }

    dv = dv.Elem()
    cv := reflect.ValueOf(context)
    if dv.Type() != cv.Type() {
        return errors.New("dest type does not match context value type")
    }

    dv.Set(cv)
    return nil
}

链接:https://go.dev/play/p/OECttqp1aVg

英文:

The argument passed to Get must be a pointer type whose element type is identical to the type in the context map. So if the value in the context map is of type *Step, then the argument's type must be **Step. Also the passed in argument cannot be nil, it can be a pointer to nil, but it itself cannot be nil.

So in your case you should do:

var stepB *Step
err := c.Get("stepA", &stepB) // pass pointer-to-pointer
if err != nil {
	panic(err)
}

And the Get method, fixed up a bit:

func (c Context) Get(stepId string, dest interface{}) error {
	context, ok := c[stepId]
	if !ok {
		return nil
	}

	dv := reflect.ValueOf(dest)
	if dv.Kind() != reflect.Ptr || dv.IsNil() {
		return errors.New("dest must be non-nil pointer")
	}

	dv = dv.Elem()
	cv := reflect.ValueOf(context)
	if dv.Type() != cv.Type() {
		return errors.New("dest type does not match context value type")
	}

	dv.Set(cv)
	return nil
}

https://go.dev/play/p/OECttqp1aVg

huangapple
  • 本文由 发表于 2022年10月12日 00:34:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/74031374.html
匿名

发表评论

匿名网友

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

确定