Telerik blogs

[Want to see JustMock in action? Register for the free webinar this Thursday, Jul 22, 11 am EST and check out What’s New in JustCode, JustMock, and OpenAccess ORM. During the live event attendees will also have the chance to win a Telerik Ultimate Collection (valued at $1999).]
 

In JustMock. The tale continues... (Part 1) we saw how we can inject prologue/epilogue code in .NET methods with JustMock. We covered the most simplest scenarios and some more complex ones as methods with ref/out parameters.

Today I will show you how to do code injections in generic methods. Basically there is nothing new. We are already familiar with:

  • AtStartOfAction
  • AtStartOfFunc
  • AtEndOfAction
  • AtEndOfFunc
  • etc.

and last time we saw how to use InjectDelete<TDelegate>(...) method as well. Here goes a demo program that I will explain in details.

 

using System;
using System.Collections.Generic;
using Telerik.CodeWeaver.Hook;
 
namespace ConsoleApplication3
{
    class Base : IDisposable
    {
        public void Dispose() {}
    }
 
    class Derived : Base { }
 
    class TargetClass1
    {
        internal V TargetMethod<U, V>(  out U u,
                              V[] v,
                              Dictionary<U, List<V>> dictUV)
            where U : IDisposable
            where V : U
        {
            throw new NotImplementedException();
        }
    }
 
    class TargetClass2<T>
    {
        internal void TargetMethod<U, V>(T t, Dictionary<U, V> dictUV)
        {
            Console.WriteLine("TargetClass2<{0}>::TargetMethod<{1}, {2}>",
                          typeof(T),
                          typeof(U),
                          typeof(V));
        }
    }
 
    class Program
    {
        delegate V PrologueDelegate1<U, V>( out U u,
                                 V[] v,
                                 Dictionary<U, List<V>> dictUV,
                                 TargetClass1 obj,
                                 out bool skipOldMethodBody)
            where U : IDisposable
            where V : U;
 
        static V PrologueInjection1<U, V>(  out U u,
                                  V[] v,
                                  Dictionary<U, List<V>> dictUV,
                                  TargetClass1 obj,
                                  out bool skipOldMethodBody)
            where U : IDisposable
            where V : U
        {
            skipOldMethodBody = true;
            u = default(U);
             
            Console.WriteLine("::PrologueInjection1<{0}, {1}>",
                           typeof(U),
                           typeof(V));
 
            return default(V);
        }
 
        delegate void PrologueDelegate2<T, U, V>(   T t,
                                       Dictionary<U, V> dict,
                                       TargetClass2<T> obj,
                                       out bool skipOldMethodBody);
 
        static void PrologueInjection2<T, U, V>(    T t,
                                       Dictionary<U, V> dict,
                                       TargetClass2<T> obj,
                                       out bool skipOldMethodBody)
        {
            skipOldMethodBody = false;
 
            Console.WriteLine("::PrologueInjection2<{0}, {1}, {2}>",
                           typeof(T),
                           typeof(U),
                           typeof(V));
        }
 
        static void Main(string[] args)
        {
            Weaver<TargetClass1>
                .AtStartOfFunc<Base, Derived[], Dictionary<Base, List<Derived>>, Derived>((_, u, v, dict) => _.TargetMethod(out u, v, dict))
                .InjectDelegate<PrologueDelegate1<Base, Derived>>(PrologueInjection1);
 
            Base arg1 = new Base();
 
            new TargetClass1().TargetMethod<Base, Derived>(out arg1, null, null);
 
            Weaver<TargetClass2<bool>>
                .AtStartOfAction<bool, Dictionary<char, double>>((_, t, dictUV) => _.TargetMethod(t, dictUV))
                .InjectDelegate<PrologueDelegate2<bool, char, double>>(PrologueInjection2);
 
            new TargetClass2<int>().TargetMethod<float, string>(0, null);
            new TargetClass2<string>().TargetMethod<char, bool>(null, null);
        }
    }
}

We use the very same API to do injections in generic methods. However this time I will start with relatively more complex scenarios. At first the syntax may look awkward but once you know the foundations everything comes to place. Lets see the target method in our first scenario.

internal V TargetMethod<U, V>(  out U u,
                       V[] v,
                       Dictionary<U, List<V>> dictUV)
    where U : IDisposable
    where V : U
{
    throw new NotImplementedException();
}

As we learned from my previous post we have to define a delegate and according the rule we have to add 2 new parameters. So here is the delegate:

delegate V PrologueDelegate1<U, V>( out U u,
                         V[] v,
                         Dictionary<U, List<V>> dictUV,
                         TargetClass1 obj,
                         out bool skipOldMethodBody)
    where U : IDisposable
    where V : U;

Lets recall that the next-to-last parameter holds the instance on which the target method is called so it must have type TargetClass1. Lets recall that the last parameter is flow control flag. Once the delegate is defined we have to define our prologue injection.

static V PrologueInjection1<U, V>(  out U u,
                         V[] v,
                         Dictionary<U, List<V>> dictUV,
                         TargetClass1 obj,
                         out bool skipOldMethodBody)
    where U : IDisposable
    where V : U
{
    skipOldMethodBody = true;
    u = default(U);
     
    Console.WriteLine("::PrologueInjection1<{0}, {1}>",
                    typeof(U),
                    typeof(V));
 
    return default(V);
}

Here we define that we want to print a simple trace and to skip the target method body (as it throws NotImplementedException). So far nothing new. Lets see how we define our injection.

Weaver<TargetClass1>
    .AtStartOfFunc<Base, Derived[], Dictionary<Base, List<Derived>>, Derived>((_, u, v, dict) => _.TargetMethod(out u, v, dict))
    .InjectDelegate<PrologueDelegate1<Base, Derived>>(PrologueInjection1);

The syntax looks heavy especially the second line. But looking closely we see that it just resembles the signature of the target method. Actually it follows the rules defined by System.Func<...> delegate and we substitute the generic type parameters with concrete types. The only caveat is the second type parameter. We define it as Derived[] not as Derived. This is because the lambda expression has to match the target method signature. With little practice one should be able to define complex injections as fast as one can type ;)

After we define our injection we create new object instance and call the target method:

new TargetClass1().TargetMethod<Base, Derived>(out arg1, null, null);

Let see our second scenario. This time we have a generic method defined in generic class. There is more. The target method uses the generic types parameters from the declaring type.

class TargetClass2<T>
{
    internal void TargetMethod<U, V>(T t, Dictionary<U, V> dictUV)
    {
      ....
    }
}

A reasonable question arises: how do we define a delegate for such method? One of the options is to include the generic type parameters of the declaring type as well. Weaver library takes this approach. So the delegate looks like:

delegate void PrologueDelegate2<T, U, V>(   T t,
                               Dictionary<U, V> dict,
                               TargetClass2<T> obj,
                               out bool skipOldMethodBody);

First we include the generic type parameters of the declaring type (in the order they are defined) then we include the generic type parameters of the target method (in the order they are defined). We follow the rule for adding 2 new parameters – one that hold the object instance on which the target method is called and one for the flow control flag. As we can see there is nothing new.

Here is the injection definition

Weaver<TargetClass2<bool>>
     .AtStartOfAction<bool, Dictionary<char, double>>((_, t, dictUV) => _.TargetMethod(t, dictUV))
     .InjectDelegate<PrologueDelegate2<bool, char, double>>(PrologueInjection2);

and here is actual method call:

new TargetClass2<int>().TargetMethod<float, string>(0, null);

Wait a second! In the first scenario we did injection for Base and Derived types and called the target method for the very same types. However in the second scenario we defined injection for <bool, char, double> tuple and called the target method for <int, float, string> tuple. Well the actual types used for injection definition serve only one goal – to allow the compiler to compile you code. As we saw in the first scenario there are constraints on the generic type parameters. So we have to satisfy these constraints. I would recommend using <object, object, …, object> tuple where possible. This reduces the noise in your source code while keep it consistent.


 

So far we saw to do prologue/epilogue injections for most kinds of instance methods. Next time I will post how to do injections for static methods, properties and unsafe methods. Stay tuned.

Register for a free webinar this Thursday, Jul 22, 11 am EST and check out What’s New in JustCode, JustMock, and OpenAccess ORM. During the live event attendees will also have the chance to win a Telerik Ultimate Collection (valued at $1999).


About the Author

Mehfuz Hossain

works as the Technical Advisor in DevTools Division . He is passionate playing around with the latest bits. He has been a Microsoft MVP, author of OS projects like LinqExtender and LINQ to flickr and most recently lighter (nodejs + mongo blogging engine). Prior to working at Telerik , Mehfuz worked as a core member in many high volume web applications including Pageflakes that is acquired by Live Universe in 2008. He is a frequent blogger and was also a contributor and site developer at dotnetslackers.com.

@mehfuzh

Comments

Comments are disabled in preview mode.