65.9K
CodeProject is changing. Read more.
Home

Collect values for all instance fields of a given type

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.71/5 (3 votes)

Jun 13, 2016

CPOL

1 min read

viewsIcon

6893

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 flagsFor 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

Collect values for all instance fields of a given type - CodeProject