Interception in .NET – Part 3: Static Interception

Updated: see the last post in the series here.

Introduction

Part three of a series on interception in the .NET world. See the first part here and the second here.

This time I’m going to talk about static interception using PostSharp as an example. Static interceptors use information provided at code writing time – aspects – to do IL weaving. This basically means that after the .NET assembly is compiled, PostSharp rewrites the produced IL to apply the changes intended by the code developer, such as method interception. This is a good thing, as you can intercept types and methods regardless or not if they are virtual, static, implement some interface, etc.

This won’t be a full coverage of PostSharp, of course – which has far more than just interception – , but I include it in this series so that we see how it compares to other techniques.

Example Using PostSharp

Imagine you want to retry a certain method a number of times, this may be because what it does is sensible to network failures, or any other reason. In PostSharp, you would write a method interceptor aspect as this:

[PSerializable]
public class RetryOnExceptionAttribute : MethodInterceptionAspect
{
    public int MaxRetries { get; set; }

    public override void OnInvoke(MethodInterceptionArgs args)
    {
        int retriesCounter = 0;

        while ( true )
        {
            try
            {
                base.OnInvoke(args);
                return;
            }
            catch (Exception e)
            {
                retriesCounter++;
                if (retriesCounter > this.MaxRetries) throw;

                Console.WriteLine(
                    "Exception during attempt {0} of calling method {1}.{2}: {3}",
                    retriesCounter, args.Method.DeclaringType, args.Method.Name, e.Message);
            }
        }
    }
}

Note that you need to reference the Nuget package PostSharp.Patterns.Diagnostics, or any other that contains the PostSharp core assemblies.

And you could then apply it to some method:

public class CustomerService
{
    [RetryOnException(MaxRetries = 5)]
    public void Save(Customer customer)
    {
        // Database or web-service call.
    }
}

After Visual Studio (or, rather, MSBuild), builds your assembly, PostSharp pops in: it reads definitions present in the PostSharp.Custom.targets file and modifies that assembly to call the RetryOnExceptionAttribute’s OnInvoke method around the Customer.Save method, because the aspect was applied to it.

The MethodInterceptionAspect is not the only aspect class, there’s also:

After PostSharp modifies the assembly, it will no longer resemble your code: if you decompile it, you will see other stuff in there, like, a call to RetryOnExceptionAttribute.OnInvoke. Something like this:

[DebuggerTargetMethod(0x600000d), DebuggerBindingMethod(0x6000010)]
public void Save(Customer customer)
{
    try
    {
        Arguments<Customer> arguments = new Arguments<Customer> {
            Arg0 = customer
        };
        MethodInterceptionArgsImpl args = new MethodInterceptionArgsImpl(this, arguments) {
            DeclarationIdentifier = new DeclarationIdentifier(0x78acef3000030000L),
            Method = <>z__a_1._2,
            TypedBinding = <Save>c__Binding.singleton
        };
        <>z__a_1.a1.OnInvoke(args);
    }
    finally
    {
    }
}

Although hard to understand, rest assured that it does what you expect! Winking smile There are a bunch of additional types generated, but it’s really irrelevant to look at them.

Alternatives to PostSharp

There are some commercial alternatives and then there’s Mono.Cecil. Implemented as part of the Mono project – meaning, free and open source – it allows you to do low level IL weaving, but you should know what you are doing as there are none of the nice wrappers that PostSharp provides.

There are some interesting projects that make Mono.Cecil easier to use, and one of them is Fody. It is basically a wrapper around it, and is also open source. If you want to do things yourself, give it a try!

Conclusion

PostSharp is very powerful, as is IL weaving in general, but sometimes we need more than static interception. Here’s where dynamic interception kicks in. In the next post, I will start describing a framework for dynamic interception with a number of implementations. Stay tuned!

Detecting Default Values of Value Types

Func<object, bool> func = (obj) => obj.Equals(default(Int32));

Func<object, bool> func = (obj) => obj.Equals(default(Int32));

Introduction

I don’t know if this happened to you: the need to find out if some instance of a class is the class’ default value. For reference types – which include nullables -, it is always null, and for value types, it is the value that you get if you do not initialize a field of that type or if you call its default parameterless constructor – false for Boolean, 0 for numbers, the 0 member of an enumeration, etc. So, the problem is, how can we tell if some instance represents this default value, dynamically, that is, for any value type, not just a specific one.

Again, nothing special, just the result of a lazy Saturday afternoon, still, hope you see some value in it! Winking smile

Take 0: Direct Comparison

How would we compare a built-in type instance, like int, with its default value? Just compare it with 0. This is useful to see how each of the other techniques compare to it.

static bool IsDefaultDirect(int obj)

{

    return obj.Equals(0);

}

Take 1: Comparison With a New Instance

An easy way is to compare the value we have with a new instance of the same type, like this:

static bool IsDefaultUsingConstruction(ValueType obj)

{

    return obj.Equals(Activator.CreateInstance(obj.GetType()));

}

Activator.CreateInstance knows how to create a default instance of a value type, so we’re good.

Take 2: Using Generics Directly

Another option is to use the ability to compare a generic variable with the default value for the generic type. This cannot be used in exactly the same way as #1, because here we need to explicitly call the comparison method with a generic parameter:

static bool IsDefaultUsingGeneric<T>(T obj) where T : struct

{

    return obj.Equals(default(T));

}

Notice the struct constraint, it is exactly the same as declaring the parameter as ValueType, because it is the base class for all value types.

Take 3: Using Generics Dynamically

You can make the previous example dynamic, with the cost of an additional method invocation, like this:

static bool IsDefaultUsingReflection(ValueType obj)

{

    //cache this, please

    var isDefaultUsingGenericMethod = typeof(Program).GetMethod("IsDefaultUsingGeneric", BindingFlags.Static | BindingFlags.NonPublic);

    var method = isDefaultUsingGenericMethod.MakeGenericMethod(obj.GetType());

    return (bool) method.Invoke(null, new object[] { obj });

}

Take 4: Using a LINQ Expression Bound to a Specific Type

Another option is to dynamically compile a LINQ expression that performs the comparison, something like this:

Func<T, bool> func = (obj) => obj.Equals(default(T));

We can create this expression dynamically, and bind it to the desired value type:

static bool IsDefaultUsingLinq(ValueType obj)

{

    var argType = obj.GetType();

    var arguments = new Expression[] { Expression.Default(argType) };

    var paramExpression = Expression.Parameter(argType, "x");

 

    var equalsMethod = argType.GetMethod("Equals", new Type[] { argType });

    var call = Expression.Call(paramExpression, equalsMethod, arguments);

 

    var lambdaArgType = typeof(Func<,>).MakeGenericType(argType, typeof(bool));

    var lambdaMethod = LambdaMethod.MakeGenericMethod(lambdaArgType);

 

    var expression = lambdaMethod.Invoke(null, new object[] { call, new ParameterExpression[] { paramExpression } }) as LambdaExpression;

 

    //cache this, please

    Delegate func = expression.Compile();

 

    return (bool)func.DynamicInvoke(obj);

}

Take 5: Using a LINQ Expression Bound to Object

A very similar option to #4 is to use Object.Equals instead of the value type’s specific Equals method, like this:

Func<object, bool> func = (obj) => obj.Equals(default(int));

Of course, the int parameter depends on the actual type of the value type parameter being passed:

static readonly MethodInfo LambdaMethod = typeof(Expression).GetMethods(BindingFlags.Static | BindingFlags.Public).First(x => x.Name == "Lambda" && x.GetParameters()[1].ParameterType == typeof(ParameterExpression[]));

static readonly MethodInfo EqualsMethod = typeof (object).GetMethod("Equals", BindingFlags.Instance | BindingFlags.Public);

 

static bool IsDefaultUsingLinqObject(ValueType obj)

{

    var argType = typeof(object);

    var arguments = new Expression[] { Expression.Convert(Expression.Default(obj.GetType()), argType) };

    var equalsMethod = EqualsMethod;

    var paramExpression = Expression.Parameter(argType, "x");

    var call = Expression.Call(paramExpression, equalsMethod, arguments);

    var lambdaArgType = typeof(Func<object, bool>);

    var lambdaMethod = LambdaMethod.MakeGenericMethod(lambdaArgType);

    var expression = lambdaMethod.Invoke(null, new object[] { call, new ParameterExpression[] { paramExpression } }) as Expression<Func<object, bool>>;

 

    //cache this, please

    Func<object, bool> func = expression.Compile();

 

    return func(obj);

}

Because the comparison expression, of type Func<object, bool>, is strongly typed, we avoid the need to call Delegate.DynamicInvoke, the performance increases substantially.

Take 6: Using Formatter Services

A long, long time ago, in a distance galaxy, I already mentioned, en passant, the usage of FormatterServices.GetUninitializedObject to create instances of a type. Picking up example #1, let’s replace Activator.CreateInstance by FormatterServices.GetUninitializedObject and see the gains:

static bool IsDefaultUsingFormatterServices(ValueType obj)

{

    return obj.Equals(FormatterServices.GetUninitializedObject(obj.GetType()));

}

Take 7: Using a LINQ Expression Bound to a Specific Type and Using Invocation Through Dynamics

What a long name… Smile Well, this one is identical to #4, but without Delegate.DynamicInvoke. Instead, I make use of the dynamic type’s late binding to invoke the delegate, which results in even better performance:

static readonly MethodInfo LambdaMethod = typeof(Expression).GetMethods(BindingFlags.Static | BindingFlags.Public).First(x => x.Name == "Lambda" && x.GetParameters()[1].ParameterType == typeof(ParameterExpression[]));

 

static bool IsDefaultUsingLinqAndDynamic(ValueType obj)

{

    var argType = obj.GetType();

    var arguments = new Expression[] { Expression.Default(argType) };

    var paramExpression = Expression.Parameter(argType, "x");

    var equalsMethod = argType.GetMethod("Equals", new Type[] { argType });

    var call = Expression.Call(paramExpression, equalsMethod, arguments);

    var lambdaArgType = typeof(Func<,>).MakeGenericType(argType, typeof(bool));

    var lambdaMethod = LambdaMethod.MakeGenericMethod(lambdaArgType);

    var expression = lambdaMethod.Invoke(null, new object[] { call, new ParameterExpression[] { paramExpression } }) as LambdaExpression;

 

    //cache this, please

    Delegate func = expression.Compile();

 

    dynamic arg = obj;

    dynamic del = func;

 

    return del(arg);

}

Measuring

I put in two methods for measuring calls and doing averages:

static long MeasureTicks(Action action)

{

    var watch = Stopwatch.StartNew();

 

    action();

 

    return watch.ElapsedTicks;

}

 

static float Measure(int times, Action action)

{

    var avg = 0L;

 

    for (var i = 0; i < times; ++i)

    {

        avg += MeasureTicks(action);

    }

 

    return (float)avg / times;

}

I used a Stopwatch to obtain the ElapsedTicks of the method to be exercised. I changed the methods I presented, namely, #4, #5 and #7, so as to cache the types and delegates created dynamically, this is crucial, and I leave that as an exercise to you – just remember that each method can potencially be called with different values, of different types. Then I added a warm-up step, which exercises the code using an integer parameter:

static void Warmup(int value)

{

    var times = 1;

    Measure(times, () => IsDefaultDirect(value));

    Measure(times, () => IsDefaultUsingConstruction(value));

    Measure(times, () => IsDefaultUsingGeneric(value));

    Measure(times, () => IsDefaultUsingReflection(value));

    Measure(times, () => IsDefaultUsingLinq(value));

    Measure(times, () => IsDefaultUsingLinqObject(value));

    Measure(times, () => IsDefaultUsingFormatterServices(value));

    Measure(times, () => IsDefaultUsingLinqAndDynamic(value));

}

In the past, I learned that a warm-up method – or lack of it – makes a huge difference.

I executed each option 100 times and got its results:

static void Measure()

{

    var times = 100;

    var value = 100;

 

    Warmup(value);

 

    var m0 = Measure(times, () => IsDefaultDirect(value));

    var m1 = Measure(times, () => IsDefaultUsingConstruction(value));

    var m2 = Measure(times, () => IsDefaultUsingGeneric(value));

    var m3 = Measure(times, () => IsDefaultUsingReflection(value));

    var m4 = Measure(times, () => IsDefaultUsingLinq(value));

    var m5 = Measure(times, () => IsDefaultUsingLinqObject(value));

    var m6 = Measure(times, () => IsDefaultUsingFormatterServices(value));

    var m7 = Measure(times, () => IsDefaultUsingLinqAndDynamic(value));

}

The results I got were:


Method Ticks Difference
#0: Direct Comparison 1.82 131.88%
#1: Comparison With a New Instance 1.92 139.13%
#2: Using Generics Directly 1.46 105.80%
#3: Using Generics Dynamically 6.9 500%
#4: Using a LINQ Expression Bound to a Specific Type 3.05 221.01%
#5: Using a LINQ Expression Bound to Object 1.61 116.67%
#6: Using Formatter Services 1.53
#7: Using a LINQ Expression Bound to a Specific Type and Using Invocation Through Dynamics 1.38 100%

Conclusion

I was really surprised that the direct comparison is actually – at least for integers – not the best way to see if a value is the default for its type! There’s a big range in results, and I can say that I was expecting that for #3. I knew that FormatterServices.GetUninitializedObject would give better results than Activator.CreateInstance, but I imagine this cannot be used with all types, because it doesn’t run the type’s constructor, possibly skipping some default initializations. I also knew that the performance of Delegate.DynamicInvoke is less than ideal, but it was interesting to see that dynamics can improve it.

As always, I’d love to see what you have to say! Do you see flaws in my approach, or do you know of any better solutions? Fire away! Winking smile