Accessing Private Methods and Properties in Sitecore using Reflection

Oh Sitecore, you and your crazy Kernel. Such beauty and craziness all rolled into one. So much extensibility yet so many hoops you make us jump through to play with you nicely. Inevitably we end up just decompiling you just to take a look at the beauty that exists inside, but at the same time you cause pain when that extensibility is not at the point we need.

So what’s a guy to do? We resort to tactics that make us feel dirty: copy and paste!

All you Sitecore developers know what I am talking about. You need to override a pipeline, or extend an event handler or change the behaviour of some part of the interface and you try you damned hardest to extend in a clean way without touching any of those Sitecore parts, but there’s just no way you can do it. The method you need access to is Private, or only internal, marked as static or was not made access.

Even Sitecore’s own best practices tell us not to do this, so we try to keep our own code as clean as we can. At least we have the source of our own code base, and live in hope that the next upgrade will not be the one to break us, it’s only a slim chance right? The problem with slim chances though is that time passes us by, and the next project comes along and then some other poor sucker has do deal with an upgrade on a system they know little about and our own recollection is vague at best.

The answer to make all our lives that little bit easier is in that same article:

When needed, use Reflection to access private or internal Sitecore components rather than copy-paste their decompiled source code because there is a lower chance that a class name or class member name will change.

Calling Private methods

So if we need to access a private method in Sitecore we simply need to use reflection to invoke that method. Here is an example from a previous blog post:

private void RunRules(Item item)
{
    // Use reflection to invoke private method RunItemSavedRules in the  ItemEventHandler
    Type t = typeof(Sitecore.Rules.ItemEventHandler);
    t.InvokeMember("RunItemSavedRules", 
                        BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic, 
                        null, 
                        new Sitecore.Rules.ItemEventHandler(), 
                        new object[] { item } );
}

We need to pass in BindingFlags to specify what we want returned in our reflection search for methods and members. In this case, we want to search on a class Instance and all NonPublic members as well.

Safety Checks

I’ve been a little gung-ho with the above code, and there is no checking involved to make sure that I’ve actually been able to find the method using reflection before invoking it. I’d used the code for a one-time run and was confident the method did actually exist. In order to be safe and for code longevity in case of removal by Sitecore in the future we should introduce some safety checks:

var refelectedMethod = typeof(Sitecore.Rules.ItemEventHandler)
                              .GetMethod("RunItemSavedRules",
                                          BindingFlags.Instance | BindingFlags.NonPublic);

if (refelectedMethod != null)
    refelectedMethod.Invoke(new Sitecore.Rules.ItemEventHandler(), new object[] { item });
else
    Log.Error("Dang, couldn't find the method you were looking for using reflection :/", this);

Method return values

If the method you are invoking returns a value then it would be useful to get access to that! That’s pretty simple too:

//Sitecore.Pipelines.HttpRequest.ItemResolver
var refelectedMethod = typeof(ItemResolver)
                              .GetMethod("ResolveFullPath",
                                          BindingFlags.Instance | BindingFlags.NonPublic);

object returnValue = refelectedMethod.Invoke(this, new object[] { args });

Item actualValue = (Item)returnValue;

Calling Invoke returns an object containing the return value of the invoked method, we’ll just need to cast it back to its correct type.

Invoking private method on inherited class

In order to invoke the method, you must pass in an instance of the class on which it should be called. In the example above I’ve just created a new instance of the object directly when I call Invoke. If you were trying to reflect a private method from an inherited class then you could simply pass in this because all appropriate instance members are returned, including members declared directly in the class being examined and the members defined in superclasses throughout the hierarchy.

public class CustomLanguageResolver : Sitecore.Pipelines.HttpRequest.LanguageResolver
{
    public override void Process(HttpRequestArgs args)
    {
        Language languageFromRequest;

        var refelectedMethod = typeof(LanguageResolver)
                                      .GetMethod("GetLanguageFromRequest",
                                                  BindingFlags.Instance | BindingFlags.NonPublic);

        languageFromRequest = (Language)refelectedMethod.Invoke(this, new object[] { args.Context.Request });

        if (languageFromRequest != null && SomethingCustom)
        {
            // run my custom code here
        }
    }
}

Invoking Private Static methods

When invoking static methods the instance parameter is ignored so we can simply pass null along with the parameters that the method calls normally requires.

var refelectedMethod = typeof(LoginPage)
                              .GetMethod("WriteCookie",
                                          BindingFlags.Static | BindingFlags.NonPublic);

refelectedMethod.Invoke(null, new object[] { "cookieName", "cookieValue" });

The only difference is the BindingFlags which are passed in, you do not require Instance on this occasion but you do require Static.

Sitecore Helpers

Since we are talking Sitecore of course there are some helpers baked right into the Kernel. Have a peek through Sitecore.Reflection.ReflectionUtil class and you’ll find a whole bunch of CallMethod declarations with different parameter overloads, open the class in dotPeek and have a look around.

public static object CallMethod(object obj, string methodName)
{
  return ReflectionUtil.CallMethod(obj, methodName, true, true);
}

public static object CallMethod(object obj, string methodName, bool includeNonPublic, bool includeInherited)
{
  Assert.ArgumentNotNull(obj, "obj");
  Assert.ArgumentNotNullOrEmpty(methodName, "methodName");
  BindingFlags bindingAttr = BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public;
  if (includeNonPublic)
    bindingAttr |= BindingFlags.NonPublic;
  if (!includeInherited)
    bindingAttr |= BindingFlags.DeclaredOnly;
  MethodInfo method = obj.GetType().GetMethod(methodName, bindingAttr);
  Assert.IsNotNull((object) method, "Unknown method '{0}' (type: {1})", new object[2]
  {
    (object) methodName,
    (object) obj.GetType()
  });
  return ReflectionUtil.InvokeMethod(method, (object[]) null, obj);
}

**Method Signatures**
public static object CallMethod(Type type, string methodName)
public static object CallMethod(object obj, string methodName)
public static object CallMethod(object obj, string methodName, object[] parameters)
public static object CallMethod(object obj, string methodName, bool includeNonPublic, bool includeInherited)
public static object CallMethod(object obj, string methodName, bool includeNonPublic, bool includeInherited, object[] parameters)
public static object CallMethod(object obj, string methodName, bool includeNonPublic, bool includeInherited, bool includeStatic, object[] parameters)
public static object CallMethod(Type type, object obj, string methodName, bool includeNonPublic, bool includeInherited, object[] parameters)
public static object CallMethod(Type type, object obj, string methodName, bool includeNonPublic, bool includeInherited, bool includeStatic, object[] parameters)
public static object CallStaticMethod(string typeName, string methodName, object[] parameters)

Performance concerns

There’s a reason that reflection is not used very often, and the reason most developers try to avoid using it: performance! Reflection is expensive and if highly optimized and performant code is important to you then it may be better to just copy+paste the code. It’s the same argument for de-normalising data in relational databases, sometimes that duplication is justified.

Be careful where you use the code, if it’s in an httpRequestBegin pipeline processor then that piece of reflection is going to happen in every single request but a custom field will get called far less often from the content editor. Using reflection within a loop is also pretty poor practice, and what I had done in my original piece of code (further up the code stack). Since that was a one-time run, throwaway piece of code it wasn’t a big deal. If you do have to do this then make sure you cache the method reference:

List<Item> items = GetListOfItems();
var itemEventHandler = new new Sitecore.Rules.ItemEventHandler();

var refelectedMethod = typeof(Sitecore.Rules.ItemEventHandler)
                              .GetMethod("RunItemSavedRules",
                                          BindingFlags.Instance | BindingFlags.NonPublic);

foreach(Item item in items)
{
    refelectedMethod.Invoke(itemEventHandler, new object[] { item });
}

Of course, our life would be much simpler if those methods were made public or protected. But all in all, pretty straight forward in the end and much cleaner code! Just be careful!

Further reading:

Advertisements

5 comments

  1. gorhal · September 3, 2015

    Very nice

  2. Martin Davies (TwentyGotoTen) · September 3, 2015

    Good post, Will definitely think twice about copy-pasting in future, but must say I’m surprised that Sitecore actively recommends using reflection.

    • jammykam · September 3, 2015

      Without a refactor of their code, it’s the only way in unfortunately. Read the links I provided about performance, and make a judgement call on the trade off.

      But take a look at the ReflectionUtil class, you’ll notice CreateObjectFromConfig for example, so Sitecore itself is using reflection for object creation in pipelines using the factory method pattern.

  3. I think you could cache the reflected method to avoid the performance hit. My belief is that the performance hit comes in the object inspection.

    We have in the past used decompiled code. I agree it should be used as a last resort as it means that you have to review it every time you perform an upgrade to make sure that no underlying code has changed.

    • jammykam · September 5, 2015

      Yes, exactly, caching the reflected method will reduce performance concerns. I think the key is to be aware of potential performance impacts and not to use it blindly.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s