Reputation: 17535
I have a struct which represents an object in a database, something like:
type Object struct {
Id string
Field1 string
Field2 int
}
And I'd like to have a function that updates the specific field in the database whenever the field is modified, something along these lines:
func (self *Object) SetField1(value string) {
self.Field1 = value
database.Update(self.Id, "Field1", self.Field1) // pseudocode
}
Is there a way to replace the "Field1"
hard-coded string such that my code is resistant to future changes in the struct field ordering and naming?
I've poked around the reflect package, and it would be nice to be able to get the StructField that represents the field I'm working with, but it seems to require either the name of the field via hard-coded string, or the field's index in the struct (which is subject to change).
Upvotes: 2
Views: 1441
Reputation: 9336
You could validate your string by adding a method something like this:
func (self *Object) verify(field string) string {
if _, ok := reflect.TypeOf(*self).FieldByName(field); ok {
return field
}
panic("Invalid field name")
}
And then use it when passing the string to the database update
func (self *Object) SetField1(value string) {
self.Field1 = value
database.Update(self.Id, self.verify("Field1"), self.Field1) // pseudocode
}
But I would think that if you're willing to use reflection, that you'd be better off just making a generic setField
method that accepts the field as a string, and the value as a interface{}
, checks the field and value, sets the value and updates the database.
This way everything is done using the string, so it'll either work or panic, and you don't need to remember to use the .verify()
method.
Somthing like:
func (self *Object) SetField(field string, value interface{}) {
// verify field and value using reflection
// set the value using reflection
database.Update(self.Id, field, self.Field1)
}
Though I don't think this'll work on unexported fields.
Upvotes: 3
Reputation: 8350
not in the context that you're talking about. It's not passed in as a parameter, so you need some other way of specifying which of the struct's fields to be sent. The mental gap here is you're trying to treat that set function like it's a property when it's not; the key difference between a property, as seen in other languages, is that a property is bound to a specific field, whereas your SetField1
method is bound to the whole struct. That method might as well set two fields.
Generally if you're doing field-wise reflective stuff, and you want to do fancy dynamic stuff with fields, you want to use struct tags. e.g., like this:
type Object struct {
Id string `db:"id"`
Field1 string `db:"field1"`
Field2 int `db:"field2"`
}
you can access those tags like-a-this:
package main
import (
"reflect"
"fmt"
)
func main() {
var x struct {
MyField int `core:"required"`
}
t := reflect.TypeOf(x)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Println(field.Name, ":", field.Tag.Get("core"))
}
}
that's not really a full solution, but... the question in the way it's asked is impossible to do.
Upvotes: 6