3

I'm trying to create a generic class that will fire an event whenever a method is called or a property is accessed or changed. It may also fire events in response to other changes or actions being taken, but for now, that'll be it.

In order to do so, I'd like to intercept every method call and every property access/change, but I have no way of knowing exactly which methods I'm handling. There's no given interface that defines every generic type T I'll be working with, so I have to use reflection. Here's how I envision it (Trigger<T> is the class, and generic would be of type T):

public Trigger()
{
    this.generic = default(T);

    foreach (MethodInfo m in generic.GetType().GetMethods())
    {
        // This is pseudocode, since I can't just set MethodInfo to a new method
        m = delegate()
            {
                m.Invoke(this.generic, null);
                if (MethodCalled != null)
                    MethodCalled(this, eventArgs /*imagine these are valid EventArgs*/);
            };
    }
}

I realize that I've grossly oversimplified the problem. First off, I'd have to deal with parameters. Second, you can't just override a method programmatically like that. And third, I haven't even started on properties yet. Plus, I'd have to be changing these things for only the object, not the entire type, so I'm not sure how that works either.

I've done my research, and all I find is confusing content. I realize that I'm somehow supposed to be using AOP, but I've never done anything other than OOP and procedural programming, so I'm rather lost in this dense jungle of knowledge. It sounds like I'll need to use PostSharp or Unity, but I still have no clue how after looking at all this, and this, and these two, and also this (all separate links, per word).

Is there any simpler way to do this? And can I even do it without using interfaces or predefined classes?

It's generics that make my problem particularly complicated. If I could just have a class inherit from T, and then use a proxy to capture its method calls and property accesses/changes, then things would maybe be a tad simpler, though I still lack the fundamental understanding of AOP to do that. Any help you can provide would be much appreciated. If possible, please write your answer at a beginner level (though I know my OOP fairly strongly, like I said, I don't know the first thing about AOP).

Community
  • 1
  • 1
jmindel
  • 465
  • 1
  • 7
  • 16
  • I've studied this problem a bit and found that it involves code weaving. Code weaving is changing things so that calls to any method first are rerouted to this other weaved code to do it's thing. Spring.NET does this already as well as others. So if you really want to continue writing your own you should look into open source projects which already do it to learn. – JWP Mar 13 '16 at 05:29
  • @JohnPeters I don't want to write my own framework or proxy. All I want to do--as the terminology from AOP puts it, as I've learned through Spring.NET (thanks for that)--is have my `Trigger` class serve as an advisor. It would give after-method-return advice to all methods invoked from the inner object of type `T`, which every `Trigger` object would contain, that fires an event from `Trigger` with the method info (same with properties, whether getting or setting them), or something along those lines. Again, though, I've no clue where to start, even after reading about AOP. – jmindel Mar 19 '16 at 09:16

2 Answers2

0

You can do it like that using NConcern, a new open source AOP Framework on which I actively work.

public class Trigger<T> : Aspect
{
    static public event EventArgs MethodCalled;
    static private Trigger<T> m_Singleton = new Trigger<T>();

    //Auto weaving aspect
    static Trigger()
    {
        Aspect.Weave<Trigger<T>>(method => method.ReflectedType == typeof(T));
    }

    public IEnumerable<IAdvice> Advise(MethodInfo method)
    {
        //define an advice to trigger only when method execution not failed
        yield return Advice.Basic.After.Returning(() => 
        {
            if (MethodCalled != null)
            {
                MethodCalled(this, null);
            }
        });
    }
}

public class A
{
    public void Test()
    {
    }
}

int main(string[] args)
{
    Trigger<A>.MethodCalled += ...
    new A().Test();
}

You can find a similar Example code source here : Example of observation pattern implemented with NConcern

NConcern AOP Framework is a light framework working at runtime. It work with code injection avoiding factory/proxy by inheritance. It allow you to add aspect to a class by injecting code you can create using simple delegate, ILGenerator or expression tree (linq) before/after or around a method. It can handle sealed class, sealed method, virtual method or explicit/implicit interface implementation.

Into my example, I create a class derived from Aspect (abstract class).

When a class derived from Aspect, it have to implement Advise method by returning an instance of Advice (Before/After/After.Returning/After.Throwing or Around). Each can be created with Delegate or Expression to define what you need to do on method interception.

public class MyAspect : IAspect
{
    //this method is called initially (not on interception) to rewrite method body.
    public IEnumerable<IAdvice> Advise(MethodInfo method)
    {
        //this block of code means that method will be rewrite to execute a write method name to console before original code only for public methods
        if (method.IsPublic)
        {
            yield return Advice.Basic.Before(() => Console.WriteLine(method.Name));
        }
    }
}

Usage

//attach myaspect to A class. All methods of A will be passed to Advise method to process methods rewriting.
Aspect.Weave<MyAspect>(method => method.ReflectedType == typeof(A));

//detach myaspect from A class. All methods will be rewrite to give back original code.
Aspect.Release<MyAspect>(method => method.ReflectedType == typeof(A));
Tony THONG
  • 772
  • 5
  • 11
0

Without resorting to a full-on AOP framework that uses post-bulid IL weaving, you can use Castle's DynamicProxy and create an interceptor. You can find plenty of tutorials online:

For your interceptor to work, you will need to make sure your generic class's methods and properties are virtual. This allows the DynamicProxy's runtime weaving code to generate a proxy that wraps your class.

Kit
  • 20,354
  • 4
  • 60
  • 103