Accessing private fields in inherited classes

By | December 6, 2013

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 w/ the same name w/in 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!

  • rhv70

    Hey guy,

    I have some dumb questions here.

    1) In your initial example in the two try blocks: is the code actually throwing an exception or is it working and you force the exception with your Assert.Fail line? I wasn’t completely clear after reading that.
    2) At the beginning I thought the problem was that OtherClass couldn’t get at myField. Then in the text below the first code example, you write “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”. Wasn’t the goal for OtherClass to be able to get at myField? I’m sure I am missing something.
    3) Why was your coworker trying to get at that private member of a production class? Perhaps there’s some context missing here that would help me understand.

    • Brandon

      1) In my usage, I found that GetField would throw an exception if the field isn’t found. Put the first UT I show in your code along w/ those classes and remove all the Asserts. You’ll see a MissingFieldException get thrown. Unfortunately, the MSDN documentation on this method is wrong and I’ve alerted them to it.
      2) since you label myField PRIVATE, OtherClass *can’t* see it. it’s only available for viewing and usage in BaseClass. If you wanted OtherClass to be able to see/use it, you would have given it the ‘protected’ accessor.
      3) He had an object set up much like I show here, but the private variable was a collection. He wanted his UT to be able to assert that after doing some operation, that collection had changed (eg: count goes up, down, etc). If he wanted to NOT use code like this, he’d have had to change his base class to expose the property (eg: ‘protected’) just for the sake of the UT. Rather than do that, I helped him spin up this chunk of code that got him where he wanted to be with his UT.

      • rhv70

        Thanks for the reply.

        3) So in the unit test, he wanted to be able to inspect the collection but in production that collection should remain private? Is this a common occurrence, to write a unit test that needs to “break” privacy rules? If so, people should be hitting this snag all the time.

        2) Sorry, my question wasn’t about what private means. I was initially confused by the text after the first code sample and I’m clear now. My bad.

        • Brandon

          It’s not that common of an occurrence actually. But if you want to “gray box” something really quick as a way to make sure it’s doing what you want so you can return it back to a “black box” it’s a pretty quick & easy trick to just write a UT that does it for you. This is what he was after. And with no risk that exposing the property might get left that way accidentally