c# Iterate array of parent type to invoke non-polymorphic method on derived type
<p>I am using very similar loops to iterate all public fields and properties of any passed object. I determine if the field/property is decorated with a particular custom attribute; if so, an action is performed on the value of the field or property. Two loops are necessary because the method to get a field value is different from the method to get a property value.</p>

<pre class="lang-cs prettyprint-override"><code>// Iterate all public fields using reflection
foreach (FieldInfo fi in obj.GetType().GetFields())
{
  // Determine if decorated with MyAttribute.
  var attribs = fi.GetCustomAttributes(typeof(MyAttribute), true);
  if (attribs.Length == 1)
  {
    // Get value of field.
    object value = fi.GetValue(obj);
    DoAction(value);
  }
}

// Iterate all public properties using reflection
foreach (PropertyInfo pi in obj.GetType().GetProperties())
{
  // Determine if decorated with MyAttribute.
  var attribs = pi.GetCustomAttributes(typeof(MyAttribute), true);
  if (attribs.Length == 1)
  {
    // Get value of property.
    object value = pi.GetValue(obj, null);
    DoAction(value);
  }
}
</code></pre>

<p>I would like to place the loop in a single, common method so that I can instead write, more simply:</p>

<pre class="lang-cs prettyprint-override"><code>DoEachMember(obj.GetType().GetFields());
DoEachMember(obj.GetType().GetProperties());
</code></pre>

<p>This requires <code>DoEachMember()</code> to accept the <code>MemberInfo</code> type (which is the parent type of both <code>FieldInfo</code> and <code>PropertyInfo</code>). The problem is there is no <code>GetValue</code> method in the <code>MemberInfo</code> class. Both <code>FieldInfo</code> and <code>PropertyInfo</code> use different methods to get the field/property value:</p>

<pre class="lang-cs prettyprint-override"><code>public void DoEachMember(MemberInfo mi, object obj)
{
  foreach (MemberInfo mi in obj.GetType().GetProperties())
  {
    object value mi.GetValue(obj); // NO SUCH METHOD!
  }
}
</code></pre>

<p>Thus, I declare a delegate to utilize inside the loop which takes a <code>MemberInfo</code> and returns the value of that member as an object:</p>

<pre class="lang-cs prettyprint-override"><code>// Delegate to get value from field or property.
delegate object GetValue(MemberInfo mi, object obj);
</code></pre>

<h2>The Question</h2>

<p>How can I detect the type of the objects in the <code>members[]</code> array, in order to define the delegate used inside the loop? Currently, I am using the first element of the array, <code>members[0]</code>. Is this a good design?</p>

<pre class="lang-cs prettyprint-override"><code>public void DoEachMember(MemberInfo[] members, object obj)
{
  // Protect against empty array.
  if (members.Length == 0) return;

  GetValue getValue; // define delegate

  // First element is FieldInfo
  if (members[0] as FieldInfo != null)
    getValue = (mi, obj) =&gt; ((FieldInfo)mi).GetValue(obj);

  // First element is PropertyInfo
  else if (members[0] as PropertyInfo != null)
    getValue = (mi, obj) =&gt; ((PropertyInfo)mi).GetValue(obj, null);

  // Anything else is unacceptable
  else
    throw new ArgumentException("Must be field or property.");

  foreach (MemberInfo mi in members)
  {
    // Determine if decorated with MyAttribute.
    var attribs = mi.GetCustomAttributes(typeof(MyAttribute), true);
    if (attribs.Length == 1)
    {
      object value = getValue(mi, obj);
      DoStuff(value);
    }
  }
}
</code></pre>

<p>Alternatively, I could detect the type upon each iteration, but there <em>should</em> be no reason individual array members will ever differ:</p>

<pre class="lang-cs prettyprint-override"><code>foreach (MemberInfo mi in members)
{
  // ...

  object value;

  if ((var fi = mi as FieldInfo) != null)
    value = fi.GetValue(obj);

  else if ((var pi = mi as PropertyInfo) != null)
    value = pi.GetValue(obj, null);

  else
    throw new ArgumentException("Must be field or property.");

  DoStuff(value);
}
</code></pre>
