Dynamic Object Inspection and Modification with Reflection
Reflection allows a program to examine and modify the structure and data of another program at runtime. This challenge asks you to build a simplified reflection system in Go that can inspect the fields of a struct, retrieve their values, and optionally, modify them. This is useful for tasks like generic data processing, serialization/deserialization, and dynamic configuration.
Problem Description
You are tasked with creating a Reflector type in Go that can inspect and manipulate the fields of a struct. The Reflector should provide the following functionalities:
-
Inspect(s interface{}) ([]FieldInfo, error): This method takes an interface{} representing any struct as input. It should return a slice ofFieldInfostructs, each containing the field's name and its value. If the input is not a struct, it should return an error. -
SetField(s interface{}, fieldName string, newValue interface{}) error: This method takes an interface{} representing a struct, a field name (string), and a new value (interface{}). It should attempt to set the value of the specified field in the struct to the new value. If the field does not exist or the type is incompatible, it should return an error. -
FieldInfostruct: This struct should have two fields:Name stringandValue interface{}.
The Inspect method should handle various data types for the field values (e.g., strings, integers, booleans, other structs). The SetField method should handle type conversions where possible (e.g., setting an integer field to a string representation of an integer). If a type conversion is not possible, it should return an error.
Examples
Example 1:
Input:
type Person struct {
Name string
Age int
IsActive bool
}
p := Person{Name: "Alice", Age: 30, IsActive: true}
Reflector.Inspect(p)
Output:
[
{Name: "Name", Value: "Alice"},
{Name: "Age", Value: 30},
{Name: "IsActive", Value: true},
]
Explanation: The Inspect method correctly identifies and retrieves the name, age, and isActive fields and their corresponding values from the Person struct.
Example 2:
Input:
type Person struct {
Name string
Age int
}
p := Person{Name: "Bob", Age: 25}
Reflector.SetField(p, "Age", "35")
fmt.Println(p)
Output:
{Bob 35}
Explanation: The SetField method successfully modifies the Age field of the Person struct to 35. Note that the string "35" is implicitly converted to the integer 35.
Example 3: (Edge Case)
Input:
type Person struct {
Name string
Age int
}
p := Person{Name: "Charlie", Age: 40}
Reflector.SetField(p, "Address", "123 Main St")
Output:
Error: Field Address not found
Explanation: The SetField method returns an error because the Address field does not exist in the Person struct.
Constraints
- The input struct can contain fields of any basic Go type (string, int, bool, float64) and nested structs.
- The
SetFieldmethod should handle type conversions where possible. If a conversion is not possible, it should return an error. - The
Inspectmethod should handle exported fields only (fields starting with a capital letter). - The
SetFieldmethod should only modify exported fields. - The code should be well-documented and easy to understand.
- Error handling is crucial. Return meaningful errors when appropriate.
Notes
- Use the
reflectpackage in Go to achieve reflection. - Consider using
reflect.ValueOfandreflect.TypeOfto inspect the struct and its fields. - Pay close attention to type safety when setting field values.
- Think about how to handle errors gracefully and provide informative error messages.
- The
interface{}type allows for flexibility in handling different data types, but also requires careful type assertions and conversions. - This is a simplified reflection system. A full reflection system would handle more complex scenarios, such as pointers, slices, and maps. Focus on the core functionality described above.