Click here to Skip to main content
15,113,131 members
Articles / Programming Languages / C#
Technical Blog
Posted 14 Dec 2013

Tagged as

Stats

14.5K views
3 bookmarked

Accessing Private Fields in Inherited Classes

Rate me:
Please Sign up or sign in to vote.
4.70/5 (5 votes)
14 Dec 2013CPOL2 min read
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)

Share

About the Author

BC3Tech
Software Developer (Senior)
United States United States
I'm a Sr. Software Engineer in the Seattle area primarily focused on serverless technologies in the cloud. In my free time I enjoy hiking & other adventures with my family around the Puget Sound and the country! You can find out more about me at my homepage: http://bc3.tech/brandonh

Comments and Discussions

 
GeneralA neat solution to a problem that shouldn't exist Pin
John Brett15-Dec-13 23:27
MemberJohn Brett15-Dec-13 23:27 
GeneralRe: A neat solution to a problem that shouldn't exist Pin
BC3Tech1-Jan-14 16:51
MemberBC3Tech1-Jan-14 16:51 
I had GetField at first. If I remember right, it throws an exception when the field isn't found. Instead of branching logic by exception, I elected to use LINQ and check for 'default' value as an indicator that the field wasn't found.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.