Skip to content

Compile time decorator pattern via IL rewriting

License

Notifications You must be signed in to change notification settings

halittiryaki/MethodDecorator

This branch is 5 commits behind sstronin/MethodDecorator:master.

Folders and files

NameName
Last commit message
Last commit date

Latest commit

b159f78 · Aug 30, 2017
Mar 16, 2014
Apr 2, 2013
Aug 23, 2017
Aug 30, 2017
Aug 22, 2017
Aug 30, 2017
Aug 25, 2017
Aug 30, 2017
Apr 19, 2015
Jun 16, 2013
Apr 18, 2016
Mar 30, 2013
Nov 5, 2014
May 22, 2017
Aug 22, 2017
May 29, 2017
Mar 2, 2014
Mar 2, 2014

Repository files navigation

Chat on Gitter NuGet Status

Icon

The nuget package

https://nuget.org/packages/MethodDecorator.Fody/

PM> Install-Package MethodDecorator.Fody

This is an add-in for Fody

Compile time decorator pattern via IL rewriting

Introduction to Fody

Your Code

// Atribute should be "registered" by adding as module or assembly custom attribute
[module: Interceptor]

// Any attribute which provides OnEntry/OnExit/OnException with proper args
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Assembly | AttributeTargets.Module)]
public class InterceptorAttribute : Attribute, IMethodDecorator	{
    // instance, method and args can be captured here and stored in attribute instance fields
	// for future usage in OnEntry/OnExit/OnException
	public void Init(object instance, MethodBase method, object[] args) {
		TestMessages.Record(string.Format("Init: {0} [{1}]", method.DeclaringType.FullName + "." + method.Name, args.Length));
	}
	public void OnEntry() {
        TestMessages.Record("OnEntry");
    }

    public void OnExit() {
        TestMessages.Record("OnExit");
    }

    public void OnException(Exception exception) {
        TestMessages.Record(string.Format("OnException: {0}: {1}", exception.GetType(), exception.Message));
    }
}

public class Sample	{
	[Interceptor]
	public void Method()
	{
	    Debug.WriteLine("Your Code");
	}
}

What gets compiled

public class Sample {
	public void Method(int value) {
	    InterceptorAttribute attribute =
	        (InterceptorAttribute) Activator.CreateInstance(typeof(InterceptorAttribute));

		// in c# __methodref and __typeref don't exist, but you can create such IL
		MethodBase method = MethodBase.GetMethodFromHandle(__methodref (Sample.Method),
														   __typeref (Sample));

		object[] args = new object[1] { (object) value };

		attribute.Init((object)this, method, args);

		attribute.OnEntry();
	    try {
	        Debug.WriteLine("Your Code");
	        attribute.OnExit();
	    }
	    catch (Exception exception) {
	        attribute.OnException(exception);
	        throw;
	    }
	}
}

NOTE: this is replaced by null when the decorated method is static or a constructor.

IntersectMethodsMarkedByAttribute

This is supposed to be used as

// all MSTest methods will be intersected by the code from IntersectMethodsMarkedBy
[module:IntersectMethodsMarkedBy(typeof(TestMethod))]

You can pass as many marker attributes to IntersectMethodsMarkedBy as you want

[module:IntersectMethodsMarkedBy(typeof(TestMethod), typeof(Fact), typeof(Obsolete))]

Example of IntersectMethodsMarkedByAttribute implementation

[AttributeUsage(AttributeTargets.Module | AttributeTargets.Assembly)]
public class IntersectMethodsMarkedByAttribute : Attribute {
	// Required
	public IntersectMethodsMarkedByAttribute() {}

	public IntersectMethodsMarkedByAttribute(params Type[] types) {
		if (types.All(x => typeof(Attribute).IsAssignableFrom(x))) {
			throw new Exception("Meaningfull configuration exception");
		}
	}
	public void Init(object instance, MethodBase method, object[] args) {}
	public void OnEntry() {}
	public void OnExit() {}
	public void OnException(Exception exception) {}
    // Optional
    //public void OnTaskContinuation(Task task) {}
}

Now all your code marked by [TestMethodAttribute] will be intersected by IntersectMethodsMarkedByAttribute methods. You can have multiple IntersectMethodsMarkedByAttributes applied if you want (don't have idea why). MethodDecorator searches IntersectMethodsMarkedByAttribute by predicate StartsWith("IntersectMethodsMarkedByAttribute")

In case of exception in async method you "OnException" will not be called, OnTaskContinuation will be called instead.

Recent changes

Icon

Icon courtesy of The Noun Project

About

Compile time decorator pattern via IL rewriting

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C# 98.1%
  • PowerShell 1.9%