Sometimes you need to access private fields and call private methods on an object – for testing, experimentation, or to work around issues in third-party libraries.
.NET has long provided a solution to this problem: reflection. Reflection allows you to call private methods and read or write private fields from outside of the class, but is verbose and messy to write. In C# 4, this problem can be solved in a neat way using dynamic typing.
As a simple example, I’ll show you how to access internals of the List<T> class from the BCL standard library. Let’s create an instance of the List<int> type:
List<int> realList = new List<int>();
To access the internals of List<int>, we’ll wrap it with ExposedObject – a type I’ll show you how to implement:
dynamic exposedList = ExposedObject.From(realList);
And now, via the exposedList object, we can access the private fields and methods of the List<> class:
// Read a private field - prints 0 Console.WriteLine(exposedList._size); // Modify a private field exposedList._items = new int[] { 5, 4, 3, 2, 1 }; // Modify another private field exposedList._size = 5; // Call a private method exposedList.EnsureCapacity(20);
Of course, _size, _items and EnsureCapacity() are all private members of the List<T> class! In addition to the private members, you can still access the public members of the exposed list:
// Add a value to the list exposedList.Add(0); // Enumerate the list. Prints "5 4 3 2 1 0" foreach (var x in exposedList) Console.WriteLine(x);
Pretty cool, isn’t it?
How does ExposedObject work?
The example I showed uses ExposedObject to access private fields and methods of the List<T> class. ExposedObject is a type I implemented using the dynamic typing feature in C# 4.
To create a dynamically-typed object in C#, you need to implement a class derived from DynamicObject. The derived class will implement a couple of methods whose role is to decide what to do whenever a method gets called or a property gets accessed on your dynamic object.
Here is a dynamic type that will print the name of any method you call on it:
class PrintingObject : DynamicObject { public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, out object result) { Console.WriteLine(binder.Name); // Print the name of the called method result = null; return true; } }
Let’s test it out. This program prints “HelloWorld” to the console:
class Program { static void Main(string[] args) { dynamic printing = new PrintingObject(); printing.HelloWorld(); return; } }
There is only a small step from PrintingObject to ExposedObject – instead of printing the name of the method, ExposedObject will find the appropriate method and invoke it via reflection. A simple version of the code looks like this:
class ExposedObjectSimple : DynamicObject { private object m_object; public ExposedObjectSimple(object obj) { m_object = obj; } public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, out object result) { // Find the called method using reflection var methodInfo = m_object.GetType().GetMethod( binder.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); // Call the method result = methodInfo.Invoke(m_object, args); return true; } }
This is all you need in order to implement a simple version of ExposedObject.
What else can you do with ExposedObject?
The ExposedObjectSimple implementation above illustrates the concept, but it is a bit naive – it does not handle field accesses, multiple methods with the same name but different argument lists, generic methods, static methods, and so on. I implemented a more complete version of the idea and published it as a CodePlex project: http://exposedobject.codeplex.com/
You can call static methods by creating an ExposedClass over the class. For example, let’s call the private File.InternalExists() static method:
dynamic fileType = ExposedClass.From(typeof(System.IO.File)); bool exists = fileType.InternalExists("somefile.txt");
You can call generic methods (static or non-static) too:
dynamic enumerableType = ExposedClass.From(typeof(System.Linq.Enumerable)); Console.WriteLine( enumerableType.Max<int>(new[] { 1, 3, 5, 3, 1 }));
Type inference for generics works too. You don’t have to specify the int generic argument in the Max method call:
dynamic enumerableType = ExposedClass.From(typeof(System.Linq.Enumerable)); Console.WriteLine( enumerableType.Max(new[] { 1, 3, 5, 3, 1 }));
And to clarify, all of these different cases have to be explicitly handled. Deciding which method should be called is normally done by the compiler. The logic on overload resolution is hidden away in the compiler, and so unavailable at runtime. To duplicate the method binding rules, we have to duplicate the logic of the compiler.
You can also cast a dynamic object either to its own type, or to a base class:
List<int> realList = new List<int>(); dynamic exposedList = ExposedObject.From(realList); List<int> realList2 = exposedList; Console.WriteLine(realList == realList2); // Prints "true"
So, after casting the exposed object (or assigning it into an appropriately typed variable), you can get back the original object!
Updates
Based on some of the responses the article is getting, I should clarify: I am definitely not suggesting that you use ExposedObject regularly in your production code! That would be a terrible idea.
As I said in the article, ExposedObject can be useful for testing, experimentation, and rare hacks. Also, it is an interesting example of how dynamic typing works in C#.
Also, apparently Mauricio Scheffer came up with the same idea almost a year before me.
Read more of my articles:
Gallery of processor cache effects
What really happens when you navigate to a URL
Human heart is a Turing machine, research on XBox 360 shows. Wait, what?
And if you like my blog, subscribe!
Err that doesn’t look like a good thing on the whole.
Internals are generally internal for a reason.
Generally, yes. But there are extenuating circumstances, as the man said. Besides, the man didn’t argue that it was a good thing on the whole, he just showed you how it _could_ be done. Microsoft probably does stuff like this behind the scenes 20 times a day – not that thats a good thing on the whole, either !
While I am a .Net developer to the core, most dynamic languages don’t have a real concept of access modifiers. You can re-write any part of Ruby you want, and JavaScript is wide open for modification whenever you feel like it.
I am not a Ruby guy, but I use JavaScript a lot, and the complete freedom to change behavior hasn’t screwed me yet. In fact, it opens up a lot of possibilities.
Cliff jumping into a lake isn’t as safe as the diving board at your public pool; cliff diving is certainly a lot more fun!
[…] in Programming Igor Ostrovsky made an interesting post on his blog, where he implements an ExposedObject class, which allows to take the private fields of an object […]
Mark, internals are usually best left untouched yes, that’s a core concept of OO programming, duh
I could see how this code could be useful in a debugging session where you don’t have access to the code and you want to reflect over the hidden properties in order to identify an issue with a vendors library.
Handy stuff to know.
I’ve been using this technique for some time, it’s cool to see that someone got the same idea and wrote a proper implementation
Nice work!!
I had this idea 1 week ago… Way tooo late
And works PERFECT for testing!
And ONLY for testing!
cu
Yeah, I use it for testing as well.
BTW, nice blog that you have there.
class ExposedObjectSimple : DynamicObject
{
private object m_object;
public ExposedObjectSimple(object obj)
{
m_object = obj;
}
public override bool TryInvokeMember(
InvokeMemberBinder binder, object[] args, out object result)
{
// Find the called method using reflection
var methodInfo = m_object.GetType().GetMethod(
binder.Name,
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
// Call the method
result = methodInfo.Invoke(m_object, args);
return true;
}
}
This does not work in VS2010, VS2013, VS2015 (and possibly won’t work in versions above either).
I guess Microsoft has patched the security risk.