http://bojordan.com/log/?p=602In the interest of speed, I’ll start with a list of truths I won’t be discussing at length:
- Some day you will need to address a problem in someone else’s libraries without the luxury of patching and recompiling their code.
- Most people know .NET reflection allows you to interact with types you didn’t have access to at compile time. It also allows you to interact with types you don’t have permission to directly address in code (internal classes, private members, etc.).
- .NET Reflector is an essential tool for any C# developer who uses someone else’s API. Which is everybody.
Using reflection to read members and invoke methods is easy:
- Get an instance of System.Type from your object. All objects have a GetType() method.
- Call Type.InvokeMember(), passing in the instance you’re wanting to manipulate as well as the name of the member and access flags.
Real-world
The XNA framework is a delightful graphics and games programming API by Microsoft, which is free (as in beer) but not open-sourced, and is officially unsupported. I play a bit with XNA and recently came across a serious performance killer in XNA 2.0 (which I’ve been assured is fixed in 3.0 final;
forum post here and
bug report here). Basically, there is a leaky dictionary embedded two internal classes deep that never gets cleaned up, framerate goes poop in a little while if you’re loading and unloading content like crazy.
So, inside the public GraphicsDevice class, there is an instance of an internal DeviceResourceManager, and inside this is the collection of internal ResourceData structs which never gets cleaned up. Once the content that’s being tracked has been disposed, its ResourceData instance can go away, so we’re going to periodically poke into this collection and flush items that are slowing down access to the collection.
In our code, we have easy access to the public GraphicsDevice. So, let’s get access to an instance of the internal DeviceResourceManager inside of it, which is a private instance named “pResourceManager”:
Type graphicsDeviceType = this.GraphicsDevice.GetType();
object deviceResourceMgrInst =
graphicsDeviceType.InvokeMember("pResourceManager",
BindingFlags.NonPublic | BindingFlags.GetField |
BindingFlags.Instance,
null,
this.GraphicsDevice, // the instance we're manipulating
null);
So, we have access to a private instance of DeviceResourceManager. Now, we go inside it in the same way to get the private collection:
Type deviceResourceMgrType = deviceResourceMgrInst.GetType();
System.Collections.IDictionary resourceDataDictionaryInst =
deviceResourceMgrType.InvokeMember("pResourceData",
BindingFlags.NonPublic | BindingFlags.GetField |
BindingFlags.Instance,
null,
deviceResourceMgrInst,
null) as System.Collections.IDictionary;
Inside the class there is a sync object for locking as we address the dictionary, but now we know how to get access to that and use it. Then, we can iterate over the objects in our dictionary and remove the ones that are no longer in use.
That’s all the magic. Use this sparingly, as the performance of addressing objects via reflection is horrendous, and it’s always dangerous to subvert API access declarations. However, it might be just the thing that saves you in a pinch.
For the curious, the continued code for the XNA 2.0 leak work-around:
Read the rest of this entry »