Collect values for all instance fields of a given type






2.71/5 (3 votes)
This tip is about how to get collect the values for all instance fields of a given type.
Introduction
This tip is about how to get collect the values for all instance fields of a given type.
Background
While working on TaskMan recently, I have encountered the problem of how to obtain the values for all instance fields of a particular type.
Giving more context: the future versions of TaskMan will make extensive use of command line flags. For now, I have made up my mind about only a few of them, and declared each flag as an instance field in my main program class (Flag<T>
is a simple data structure I use to represent a CLI command flag):
public class TaskMan { Flag<bool> _displayHelpFlag = new Flag<bool>(nameof(_displayHelpFlag), "?|help"); Flag<bool> _displayLicenseFlag = new Flag<bool>(nameof(_displayLicenseFlag), "license"); Flag<bool> _displayVersionFlag = new Flag<bool>(nameof(_displayVersionFlag), "version"); Flag<string> _priorityFlag = new Flag<string>(nameof(_priorityFlag), "p|priority"); Flag<string> _descriptionFlag = new Flag<string>(nameof(_descriptionFlag), "d|description"); // ... }
At the same time, for one particular purpose irrelevant to the point of this post, I also needed to collect all flags into a collection of the non-generic base class type, Flag
.
Of course, at first I wrote something like the following code in the constructor:
public TaskMan(/* arguments irrelevant */) { _flags = new Flag[] { _displayHelpFlag, _displayLicenseFlag, _displayVersionFlag, // ... }; }
It didn't look good enough. First, it smells of ugly code duplication – I essentially declare things twice. Second, it's bug-prone: what if I add another flag and forget to update the collection in the constructor?
Using the code
So that's what I ended up with, reflection to the rescue:
_flags = typeof(TaskMan)
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.Where(fieldInfo => typeof(Flag).IsAssignableFrom(fieldInfo.FieldType))
.Select(fieldInfo => fieldInfo.GetValue(this))
.Cast<Flag>();
Note how concise and human-readable this looks using Linq / Lambda syntax! You can even read it out aloud:
- We ask the type
TaskMan
: - "Get us all your non-public instance fields,
- where the field type is assignable to a variable of type
Flag
, - collect values from each of these fields,
- and cast them to the
Flag
type".
You can customize this snippet for your needs, substituting the encompassing type name for TaskMan
and the desired field type name for Flag
.
Also, if you're interested in public fields as well, don't forget to remove the BindingFlags.NonPublic from the GetFields call.
History
2016.06.13 – First version of the article