Partilhar via


Use C# 4.0 dynamic to drastically simplify your private reflection code

Update 8/10/2011: this project now has a home at https://github.com/davidebbo/ReflectionMagic. Also, it is installable via nuget. It's named 'ReflectionMagic'.

 

Private reflection allows you to access private and internal members in other assemblies.  Generally, it’s considered to be a bad thing to do, as it ties you to undocumented implementation details which can later break you.  Also, it’s not usable in medium trust (and neither is the technique I describe in this post).

My purpose in this post is not to encourage anyone to use private reflection in situations where you would not have done it anyway.  Instead, my purpose is to show you how to do it much more easily if you decide that you need to use it.  Putting it a different way, I’m not telling you to break the law, but I’m telling you how to break the law more efficiently if that’s what you’re into!

Ok, that was my little disclaimer to avoid getting in trouble.  My lawyer made me do it!  Or would have if I had one. :)

The scenario

So let’s look at a sample scenario.  Say you’re using an assembly that has code like this:

 public class Foo1 {    private Foo2 GetOtherClass() { ... }}internal class Foo2 {    private string SomeProp { get { .. } }}

And say you have an instance foo1 of the public class Foo1 and your evil self tells you that you want to call the private method GetOtherClass() and then get the SomeProp property off of that.

The solution using good old private reflection

Here is how you can write this code the old fashion way using private reflection.

 object foo2 = typeof(Foo1).InvokeMember("GetOtherClass",    BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod,    null, foo1, null);PropertyInfo propInfo = foo2.GetType().GetProperty("SomeProp",    BindingFlags.Instance | BindingFlags.NonPublic);string val = (string)propInfo.GetValue(foo2, null);

Sure that works, but ouch, that’s ugly!

The dynamic solution

Now to be clear, C# 4.0 dynamic is not going to help you with this out of the box, because it doesn’t support private reflection (I suppose the C# designers didn’t want to encourage that).  So if you try to write:

 string val = ((dynamic)foo1).GetOtherClass().SomeProp;

This will blow up with the error “RuntimeBinderException: 'LibraryWithPrivateMembers.Foo1.GetOtherClass()' is inaccessible due to its protection level”.

To get around this, we need to write our own DynamicObject implementation which will perform private reflection under the cover.  With that library, we can now write (after importing the PrivateReflectionUsingDynamic namespace):

 string val = foo1.AsDynamic().GetOtherClass().SomeProp;

So we call AsDynamic() on our object (it’s an extension method), which gets us our special DynamicObject implementation.  Once we have that, we can happily write a tiny bit of nice looking dynamic code to replace the ugly reflection mess!

How does it all work

I attached a full VS2010 solution with unit test coverage, so I’m not going to show you the whole code here (it’s over 200 lines).  Instead, I’ll describe it at a high level.

I actually wrote a few other posts before where I wrote custom DynamicObject implementation (named PrivateReflectionDynamicObject).  The general idea is that you override various methods that get called by the dynamic engine when it needs to execute the code.  e.g. when it needs to get a property value, it calls your TryGetMember method.  To call a method, it calls your TryInvokeMember method.

The difference between my previous posts and what I did here is that PrivateReflectionDynamicObject is much more complete, because it’s trying to handle most of the things you might want to using private reflection.  Specifically, it handles:

  • Getting and setting properties
  • Getting and setting fields (note that properties and fields are the same thing in the dynamic world, but different in the reflection world)
  • Calling methods
  • Getting and setting values into arrays
  • Getting and setting values into indexed properties (e.g. string this[string s] { … })
  • Type conversions

The implementation of all this is simply to turn around and use private reflection.  So in the end, you’re executing mostly the same code as if you had done it manually, but with a lot less pain.

One interesting thing to note is that objects returned by this logic need to be themselves wrapped into a PrivateReflectionDynamicObject.  This is necessary in order to able to write foo1.AsDynamic().GetOtherClass().SomeProp.  Otherwise, GetOtherClass() would just return a Foo2 object, and you would not be able to access the SomeProp property.  In a twisted way, you might call that a fluent API.

And finally, then there is the AsDynamic extension method, which makes it easy to access this functionality.  It just has:

 public static class PrivateReflectionDynamicObjectExtensions {    public static dynamic AsDynamic(this object o) {        return PrivateReflectionDynamicObject.WrapObjectIfNeeded(o);    }}

Usually, I’m hesitant to add extension methods to object, but in this case it seemed appropriate, since you can potentially do private reflection on any object.  Also, it’s in a specific namespace, so the method only shows up if you choose to import it.

You can unzip the attach solution and try it on VS2010.  I have a somewhat newer build, but I think it should run on beta 2.

PrivateReflectionUsingDynamic.zip

Comments

  • Anonymous
    January 18, 2010
    You make it sound like your solution to the problem would run in medium trust. But since you've just abstracted the reflection, I guess the same caveats apply?

  • Anonymous
    January 18, 2010
    @RichB: nope, won't run in medium trust any more than straight reflection, so didn't mean to imply that.  I changed the first paragraph to make that clear.

  • Anonymous
    January 20, 2010
    Wait, i'm confused. You made it clear that both solutions have the same caveats. And strongly implied from the way i read that using Dynamic is "better" for syntactic reasons. Yet to get your sugar you've written a custom class and an extension method?

  • Anonymous
    January 20, 2010
    @M8: right, it takes a helper library to get this improved syntax.  The point is that if you just use that library, it just works and the only thing you are concerned with is the simpler syntax.

  • Anonymous
    January 20, 2010
    Yup sure, but internally your library could use reflection yes? The callers won't / don't care. What makes Dynamic inherently better for the task essentially?  

  • Anonymous
    January 20, 2010
    Let me clear that last comment up. What i was getting at was that you could package something as syntactically sweet which used reflection?

  • Anonymous
    January 20, 2010
    @M8: but the library implementation does use reflection internally.  It's just a wrapper that gives you reflection with the much simpler dynamic syntax.

  • Anonymous
    February 05, 2010
    This is great! This way I can write test code without using InternalsVisibleTo(testAssembly) in the real code. Private reflection was always available but was previously ruled out because the reflection syntax was too ugly and obscured the intent of the test. Dynamic is awesome here. -Max

  • Anonymous
    October 26, 2010
    Thanks a million for this.  I was really stuck trying to get a private property out of one of the framework attributes and this does the trick.

  • Anonymous
    June 14, 2011
    I need help making this example code work on objects with multiple indexers (aka, Item properties). The ol' DataTable class has multiple indexers.

  • Anonymous
    June 15, 2011
    @Brannon: this is not currently supported, but potentially could be.

  • Anonymous
    October 26, 2011
    Excellent solution. Thanks

  • Anonymous
    July 21, 2012
    When I try to cast the reflected properties I get an exception because the type of the property is not IConvertible. Is there a way around this?

  • Anonymous
    July 22, 2012
    @pj please open an issue on github.com/.../ReflectionMagic with more details about what you are doing.

  • Anonymous
    May 16, 2013
    To quote Chris van der Steeg: "I know it’s the wrong path to choose, but did it anyway".