65.9K
CodeProject is changing. Read more.
Home

Accessing Private Fields in Inherited Classes

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.73/5 (6 votes)

Dec 14, 2013

CPOL

2 min read

viewsIcon

19401

How to access private fields in inherited classes

Introduction

A co-worker of mine was working on some UTs for his current setup and wanted to invoke/access private members on some of his production objects. I introduced him to the PrivateObject type in .NET to get his work done. That all went well until he wanted to get at a private field that was in a class that his main object inherited from. Turns out that’s not something built-in to the reflection/PrivateObject stack of .NET.

Consider the following set of simple classes:

class BaseClass
{
    private string myField = "base class my field";
}

class OtherClass : BaseClass { }

Now consider this unit test:

  1: [TestMethod]
   2: public void PrivateObjectTest()
   3: {
   4:     var po = new PrivateObject(new OtherClass());
   5:  
   6:     try
   7:     {
   8:         po.GetField("myField", System.Reflection.BindingFlags.NonPublic | 
   			System.Reflection.BindingFlags.Instance);
   9:         Assert.Fail("GetField shouldn't have worked!");
  10:     }
  11:     catch
  12:     {
  13:         Assert.IsTrue(true);
  14:     }
  15:  
  16:     try
  17:     {
  18:         po.RealType.GetField("myField", 
  		System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
  19:         Assert.Fail("GetField shouldn't have worked!");
  20:     }
  21:     catch
  22:     {
  23:         Assert.IsTrue(true);
  24:     }
  25:  
  26:     string foundFieldValue;
  27:     Assert.IsTrue(po.TryFindField<string>("myField",
  28:         System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance,
  29:         out foundFieldValue));
  30:     Console.WriteLine(foundFieldValue);
  31: }

This UT will pass, but note what it’s asserting; that your GetField calls do not work as you’d think they would. “myField” is indeed a non-public instance field on your object, but it’s on your object’s base class and therein lies the rub (for both PrivateObject, the first try/catch, and System.Reflection, the second try/catch).

How Do We Fix This?

Well a brute-force way of doing this would be to GetType().BaseType and ask it for GetField(). But then, my code has to know that the field I want to play with exists on the base type. Kind of annoying.

Extension method time!

Add this beauty to your codebase:

   1: using System;
   2: using System.Linq;
   3: using Microsoft.VisualStudio.TestTools.UnitTesting;
   4:  
   5: public static class PrivateObjectExtensions
   6: {
   7:     public static bool TryFindField<T>
   		(this PrivateObject po, string name, out T value)
   8:     {
   9:         return po.TryFindField<T>
   		(name, System.Reflection.BindingFlags.Default, out value);
  10:     }
  11:  
  12:     public static bool TryFindField<T>(this PrivateObject po, 
  		string name, System.Reflection.BindingFlags flags, out T value)
  13:     {
  14:         Type t = po.RealType;
  15:         bool found = false;
  16:         value = default(T);
  17:         do
  18:         {
  19:             var field = t.GetFields(flags)
  20:                 .FirstOrDefault(f => f.Name == name);
  21:             if (field != default(System.Reflection.FieldInfo))
  22:             {
  23:                 value = (T)field.GetValue(po.Target);
  24:                 found = true;
  25:             }
  26:             else
  27:             {
  28:                 t = t.BaseType;
  29:             }
  30:         } while (!found && t != null);
  31:  
  32:         return found;
  33:     }
  34: }

And add a few more lines to the UT:

string foundFieldValue;
Assert.IsTrue(po.TryFindField<string>("myField", 
    System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, 
    out foundFieldValue));
Console.WriteLine(foundFieldValue);

And voila!

There’s one minor gotchya here, and likely the reason that .NET doesn’t build this in automatically for you. If you have a chain of inheritance, you can have private fields with the same name within that chain. The code I have here will simply find the one “closest to the top” and return its value. If there’s one deeper, you won’t get to it. The built-in GetField methods will work on protected fields just fine – because of course you can’t get a name collision that way. So just keep that in mind.

Enjoy!