Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Accessing Private Fields in Inherited Classes

4.73/5 (6 votes)
14 Dec 2013CPOL2 min read 19.4K  
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:

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

class OtherClass : BaseClass { }

Now consider this unit test:

C#
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:

C#
 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:

C#
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!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)