简体   繁体   English

在 Go 中使用反射分配给类型定义

[英]Assigning to type definition using reflection in Go

I have setup a type called Provider that defines an integer:我设置了一个名为Provider的类型,它定义了一个 integer:

type Provider int

func (enum *Provider) Scan(raw interface{}) error {
    *enum = Provider(int(raw))
}

If I create an object, Foo, with a Provider field, like this:如果我创建一个带有Provider字段的 object,Foo,如下所示:

type Foo struct {
    Code Provider
    Value string
}

foo := &Foo {
    Code:  Provider(0),
    Value: "derp",
}

var scnr sql.Scanner
scannerType := reflect.TypeOf(&scnr).Elem()

tType := reflect.TypeOf(foo)
tField := tType.Field(0)
fmt.Printf("Field %s, of type %s, kind %s\n", 
    tField.Name, tField.Type, tField.Type.Kind())

When I run this code, I get that the field is of type Provider and its Kind is int .当我运行这段代码时,我知道该字段的类型是Provider并且它的 Kind 是int However, I cannot assign an int to a Provider using reflection because this will fail:但是,我不能使用反射将 int 分配给 Provider,因为这将失败:


getInteger() interface{} {
    return 1
}

fValue := reflect.ValueOf(foo).Elem()
vField := fValue.Field(0)
vField.Set(reflect.ValueOf(getInteger())) // panic: reflect.Set: value of type int is not assignable to type Provider

The normal solution to this would be to do reflect.ValueOf(Provider(getInteger().(int))) , however, this won't work because vField is set inside of a loop that iterates over a structure's fields and therefore will have a different type.对此的正常解决方案是执行reflect.ValueOf(Provider(getInteger().(int))) ,但是,这不起作用,因为vField设置在迭代结构字段的循环内,因此将具有一种不同的类型。 Essentially, I would like a way to detect that vField is a definition of int (ie. Provider ) rather than an int , itself;本质上,我想要一种方法来检测vFieldint的定义(即Provider )而不是int本身; so I could use reflection to cast the integer value to Provider when I set it.所以我可以在设置时使用反射将 integer 值转换为Provider

Is there a way I can make this work?有没有办法让这个工作?

The kind and type are not identical terms.种类类型不是相同的术语。 Kind of both int and Provider is int because Provider has int as its underlying type. intProvider种类都是int ,因为Providerint作为其基础类型。 But the type Provider is not identical to int .但是类型Providerint不同。 What you have is a type definition , but not a type alias.您拥有的是类型定义,而不是类型别名。 Those are 2 different things.这是两件不同的事情。 See Confused about behavior of type aliases for details.有关详细信息,请参阅对类型别名的行为感到困惑

The type of Foo.Code field is Provider , you can only assign a value of type Provider to it: Foo.Code字段的类型是Provider ,您只能为其分配一个Provider类型的值:

vField.Set(reflect.ValueOf(Provider(1)))

This works.这行得通。

Note that reflection or not, you can't assign int to Foo.Code :请注意,无论是否反射,您都不能将int分配给Foo.Code

var i int = 3
var foo Foo
foo.Code = i // Error!

The above has compile time error:以上有编译时错误:

error: cannot use i (variable of type int) as type Provider in assignment错误:不能在赋值中使用 i(int 类型的变量)作为类型 Provider

What may be confusing is that using a constant works:可能令人困惑的是,使用常量有效:

var foo Foo
foo.Code = 3 // Works!

This is because 3 is an untyped constant representable by a value of type Provider , so the constant will be converted to Provider .这是因为3是一个无类型的常量,可以由Provider类型的值表示,因此该常量将被转换为Provider

I ended up using the Convert function in conjunction with the Assignable function to do my check:我最终使用Convert function 和Assignable function 进行检查:


// Get value to assign and its type
vValue := reflect.ValueOf(getInteger())
tValue := vValue.Type()

// Check if the field can be assigned the value; if it can then do so
// Otherwise, check if a conversion can be assigned
vType := vField.Type()
if tValue.AssignableTo(vType) {
    vField.Set(vValue)
} else if vValue.CanConvert(vType) {
    vField.Set(vValue.Convert(vType))
}

This fixed my issue.这解决了我的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM