How to access private class members using Linq Expressions

This article is intended for cases where you are willing/forced to access a private member of a class.

That means you are not willing/able to change the code that defines the member.

If you need to access the member for many many instances of the class, “GetFieldAccessor” will save your cpu some time.

Properties and methods are accessible by creating “open instance delegates” via Delegate.CreateDelegate.

As discussed here: Use delegate to get values for FieldInfo, “Field access isn’t performed via a method (like getters and setters)–it’s performed with an IL instruction–so there’s nothing you can assign to a delegate. you’ll have to use the expression route to create a “block” of code (effectively IL) that can be assigned to a delegate.”

Previous to the introduction of Linq Expressions, Reflection would have been your only option.

Usage:

public class TestClass
{
    public TestClass(int i) { privateField = i; }
    private int privateField;
}
 
private void Test()
{
    var list = Enumerable.Range(0, 10000000).Select(i => new TestClass(i)).ToList();
    // old way: use FieldInfo
    var fieldInfo = typeof(TestClass).GetField("privateField"BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance);
    var w = Stopwatch.StartNew();
    for (int i = 0; i < list.Count; i++)
    {
        var val = (int)fieldInfo.GetValue(list[i]);
        if (val != i)
            throw new InvalidOperationException();
    }
    w.Stop();
    // new way: use Expression.Field
    var accessor = GetFieldAccessor<TestClassint>("privateField");
    var w2 = Stopwatch.StartNew();
    for (int i = 0; i < list.Count; i++)
    {
        var val = accessor(list[i]);
        if (val != i)
            throw new InvalidOperationException();
    }
    w2.Stop();
    // ~ 7%
    Console.WriteLine(100.0 / (w.ElapsedTicks / w2.ElapsedTicks) + "%");
}

Both variants do the expensive Reflection part only once, outside of the for-loop.

The compiled delegate takes less than 10% cpu time. (compared to Reflection)

Here is the code for GetFieldAccessor:

public static Func<T, TResult> GetFieldAccessor<T, TResult>(string fieldName)
{
    var instExp = System.Linq.Expressions.Expression.Parameter(typeof(T));
    var fieldExp = System.Linq.Expressions.Expression.Field(instExp, fieldName);
    return System.Linq.Expressions.Expression.Lambda<Func<T, TResult>>(fieldExp, instExp).Compile();
} 

One last point: If you are forced to use .NET before 3.5 but still need additional performance:

  • Use Reflection.Emit to create a method that returns the private member
  • Then use an open instance delegate