Click here to Skip to main content
13,865,831 members
Click here to Skip to main content
Add your own
alternative version


94 bookmarked
Posted 20 Sep 2007
Licenced CPOL

Have Fun Again With Custom Attributes (Part 2)

, 20 Sep 2007
Rate this:
Please Sign up or sign in to vote.
This is the second article in a series of two, with this one going into the details of the real-world implementation of custom attributes that actually add behaviors to your code, namely performance instrumentation and field validation.


This is the second article in a series of two, with this one going into the details of the real-world implementation of custom attributes that actually add behaviors to your code, namely performance instrumentation and field validation.

The first article of this series introduced some key concepts of PostSharp, based on a simple example: a tracing custom attribute derived from OnMethodBoundaryAspect. This article presents two more types of custom attributes, with two examples: a performance counter based on OnMethodInvocationAspect, and a field validation framework based on OnFieldAccessAspect.

The Two Lives of a Custom Attribute

Before jumping into the implementation of new custom attributes, let's delve into the internal workings of PostSharp Laos. PostSharp is an MSIL enhancer: it inserts itself in the build process, and modifies the output of the compiler (C#, VB.NET, J#, …). It looks for PostSharp Laos custom attributes, and modifies the methods, types, and fields to which they are applied.

To derive the maximum advantage from PostSharp Laos, it is necessary to fully understand the lifecycle of its custom attributes. They really have two lives: a first one at compile-time, inside PostSharp; and a second one at runtime.

The life cycle of a PostSharp Laos custom attribute is as follows:

  • At compile-time:
  1. For each application of the custom attribute, a new instance is created. Therefore, an instance of a custom attribute is always assigned to one and only one method, field, or type. Then, instances are initialized (method CompileTimeInitialize) and validated (method CompileTimeValidate).
  2. Custom attributes are serialized to a binary blob.
  3. They are stored in a managed resource of the output assembly.
  • At runtime:
  1. Custom attributes are deserialized from the managed resource, and each instance is initialized a second time (method RuntimeInitialize),
  2. Runtime methods (OnEntry, OnExit, …) are invoked as the method or field to which it is applied is invoked or accessed.

Okay, that's enough about the theory for now. Let's move on to the code!

Performance-Counter Custom Attribute

How do you monitor the performance of an application in a production environment where you cannot use your favorite profiler? Answer: you can use performance counters, but it requires a lot of plumbing code to instrument existing code. The solution is to encapsulate this plumbing code as an aspect, i.e., to make a custom attribute out of it, and then apply this custom attribute to every relevant method.

And what if we want to instrument a method located outside the current assembly? It's not a problem for PostSharp to apply a custom attribute to external declarations. However, since we can't modify the method, we have to intercept its call. So, instead of the OnMethodBoundary aspect, we will use the OnMethodInvocation:

public class PerformanceCounterAttribute : OnMethodInvocationAspect

    public override void OnInvocation( MethodInvocationEventArgs eventArgs )
       // Our implementation goes here.

In the eventArgs parameter, we get information about the method actually being called: eventArgs.Delegate is a delegate to the intercepted method, and eventArgs.GetArguments() gives the arguments passed to the method. PostSharp Laos expects us to put the return value in eventArgs.ReturnValue. Therefore, we can call the intercepted method as follows:

eventArgs.ReturnValue = eventArgs.Delegate.DynamicInvoke(
                                             eventArgs.GetArguments() );

1. Measuring Performance

Our performance counter should count the number of invocations and should measure the time spent in the method. Since every instance of the custom attribute is associated with one and only one intercepted method, we can store the hit count and the elapsed time as instance fields of the custom attribute.

Based on these principles, here is our first working implementation:

public class PerformanceCounterAttribute : OnMethodInvocationAspect
    private long elapsedTicks;
    private long hits;

    public override void OnInvocation( MethodInvocationEventArgs eventArgs )
        Stopwatch stopwatch = Stopwatch.StartNew();
            eventArgs.ReturnValue = eventArgs.Delegate.DynamicInvoke(
                                                  eventArgs.GetArguments() );
            Interlocked.Add( ref this.elapsedTicks, stopwatch.ElapsedTicks );
            Interlocked.Increment( ref this.hits );

2. Discovering Performance Counter Instances

It works, but how do we read the values? We simply have to expose fields in read-only public properties and build a repository of performance counter instances. We have to build this repository at runtime; therefore, we cannot do this registration in the instance constructor (which is called at compile-time), but must do it in the RuntimeInitialize method. One last thing: we have to expose the identity of the instrumented method; otherwise, how would we know to which method the performance counter relates? So, we must store the target method in a field and expose it in a read-only property. The RuntimeInitialize method is the right place to initialize this field.

Here is the code we have to add to the class:

[NonSerialized] private MethodBase method;

private static readonly List<performancecounterattribute /> instances =
              new List<performancecounterattribute />();

public override void RuntimeInitialize( MethodBase method )
   base.RuntimeInitialize( method );
   this.method = method;
   instances.Add( this );
public MethodBase Method { get { return this.method; } }

public double ElapsedMilliseconds 
  get { return this.elapsedTicks/( Stopwatch.Frequency/1000d ); } 

public long Hits { get { return this.hits; } }

public static ICollection<performancecounterattribute /> Instances 
    return new ReadOnlyCollection<performancecounterattribute />( instances ); 

3. Done. Use it.

And that's all! We can now apply our custom attribute to the methods we want to instrument. Suppose we want to measure the time spent in the System.IO namespace. We will add a performance counter to these methods, using the following piece of code:

[assembly: PerformanceCounter(AttributeTargetAssemblies = "mscorlib", 
           AttributeTargetTypes = "System.IO.*")]

And, if we instrument a small program listing the content of a folder, we get the following output:

One of the things you should be aware of is that this aspect intercepts only calls made from the current assembly. So, if you call an external method that indirectly calls an instrumented method, this indirect call won't be monitored. This limitation is inherent to the technology used by PostSharp: MSIL rewriting.

Validating Fields With Custom Attributes

So far, we've seen how to modify method bodies and how to intercept method calls. PostSharp makes it possible to intercept "get" and "set" operations on fields. One of the applications of this technique involves the validation of fields: we could make a field non-nullable or enforce a regular expression just by decorating the field with a custom attribute.

Aspects that need to intercept field accesses should derive from the OnFieldAccessAspect class. They may override the OnGetValue() and OnSetValue() methods. For field validation, we are only interested in the second method. All we have to do is perform the validation that is specific to the validator.

1. Designing an Abstract Framework

The class design of a validation framework is simple: we have basically an abstract class FieldValidationAttribute exposing an abstract method Validate() called from the overridden OnSetValue() method. By contract, the implementation of Validate() should throw an ad-hoc exception if the method is invalid. The exception will typically include the field name. So, it would be fine for the FieldValidationAttribute class to expose the name of the field to which the custom attribute instance is applied. Since this information is known at compile-time, it is initialized in the CompileTimeInitialize() method and stored in a serializable field.

One thing we have to remember is that, just like OnMethodInvocationAspect, OnFieldAccessAspect intercepts accesses to a field; it is therefore limited to the current assembly. If you follow Microsoft's recommendation and have only private fields, this is not a problem. But if you prefer to have public fields, you should ask PostSharp Laos to encapsulate fields into properties. Just override the GetOptions() method and return GenerateProperty.

Here is the complete code for the FieldValidationAttribute class:

[AttributeUsage( AttributeTargets.Field, AllowMultiple = false )]
public abstract class FieldValidationAttribute : OnFieldAccessAspect
    private string fieldName;

    public override void CompileTimeInitialize( FieldInfo field )
        base.CompileTimeInitialize( field );

        this.fieldName = field.DeclaringType.Name + "." + field.Name;

    public string FieldName { get { return this.fieldName; } }

    protected abstract void Validate( object value );

    public override sealed void OnSetValue( FieldAccessEventArgs eventArgs )
        this.Validate( eventArgs.ExposedFieldValue );

        base.OnSetValue( eventArgs );

    public override OnFieldAccessAspectOptions GetOptions()  
        return OnFieldAccessAspectOptions.GenerateProperty;


2. Checking Non-Null Fields

The "non-nullable" field aspect is the most trivial:

public sealed class FieldNotNullAttribute : FieldValidationAttribute
    protected override void Validate( object value )
        if ( value == null )
            throw new ArgumentNullException( "field " + this.FieldName );

Defining a non-nullable field is as easy as this:

class MyClass
  public string Name = "DefaultName";

3. Checking with Regular Expressions

A more challenging case is to design a custom attribute that checks regular expressions. The constructor of the custom attribute should accept the matching pattern as a parameter, as well as, optionally, a value indicating whether null values are acceptable for this field. If we want to avoid having to recompile the regular expression at each field assignment, we can store it as an instance field that we should initialize at runtime, in the RuntimeInitialize() method.

Here is a basic but working implementation of the pattern-matching field validator:

public sealed class FieldRegexAttribute : FieldValidationAttribute
    private readonly string pattern;
    private readonly bool nullable;
    private RegexOptions regexOptions = RegexOptions.Compiled;

    private Regex regex;

    public FieldRegexAttribute(string pattern, bool nullable)
        this.pattern = pattern;
        this.nullable = nullable;

    public FieldRegexAttribute(string pattern) : this(pattern, false)

    public RegexOptions RegexOptions
        get { return regexOptions; } 
        set { regexOptions = value; }

    public override void RuntimeInitialize(FieldInfo field)
        this.regex = new Regex( this.pattern, this.regexOptions);

    protected override void Validate( object value )
        if ( value == null )
            if ( !nullable )
                throw new ArgumentNullException("field " + this.FieldName);
            string str = (string) value;
            if ( !this.regex.IsMatch( str ))
                throw new ArgumentException( 
                    "The value does not match the expected pattern.");

4. Done. Use it.

We're done! We have developed custom attributes that allow us to validate fields at runtime.

Their use is very straightforward:

class MyClass
    public string Name = "DefaultName";
    public string EmailAddress;

Look at the resulting assembly, using Lutz Roeder's Reflector:

Fields have been encapsulated into properties, and if you look at the implementation of accessors, you'll see that our validating custom attributes have been invoked.

Simple. Powerful. What more could you want?


While the first article of this series introduced most of the key concepts of PostSharp Laos using a simple example based on OnMethodBoundaryAspect, the current article introduced two more aspects: OnMethodInvocation and OnFieldAccess.

We have seen the difference between OnMethodBoundaryAspect and OnMethodInvocationAspect: whereas the first actually adds a try-catch block on the target method, the second one intercepts method calls and does not modify the target method. This makes it possible to apply OnMethodInvocationAspect even on methods defined outside the current assembly. The first example exploited this feature to measure the time spent in the namespace System.IO.

The second example illustrated how to add behaviors to field behaviors. We have also seen how to generate a property around a field so that the behavior is also invoked from assemblies other than the current one.

Principally, I hope that you are now convinced that we can revisit the way we currently solve cross-cutting problems: Aspect-Oriented Programming is an elegant solution to most of these, and PostSharp Laos provides a simple and powerful technology.

Now, look at the three last projects you've worked on, and think about how much effort you could have saved with PostSharp…


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Gael Fraiteur
Web Developer
Czech Republic Czech Republic
Gael Fraiteur is a software engineer specializing in application development for large and medium accounts. He has 5 years of professional experience and more than 15 years of hobby experience in software development. Gael Fraiteur has an advanced knowledge of the .NET Framework, Oracle Server, Microsoft SQL Server and TIBCO.

Gael Fraiteur is the lead developer of PostSharp, a post-compiler for .NET that makes AOP on .NET easy both for business and system developers.

You may also be interested in...

Comments and Discussions

QuestionCalling advised methods from outside of assembly Pin
jnoody3-Apr-09 9:07
memberjnoody3-Apr-09 9:07 
QuestionIssue with 3.5 and assembly entries Pin
Damon Overboe9-Mar-09 6:40
memberDamon Overboe9-Mar-09 6:40 
AnswerRe: Issue with 3.5 and assembly entries Pin
Gael Fraiteur9-Mar-09 6:45
memberGael Fraiteur9-Mar-09 6:45 
QuestionRuntime Instrumentation Pin
Hendry Luk28-Aug-08 18:18
memberHendry Luk28-Aug-08 18:18 
AnswerRe: Runtime Instrumentation Pin
Gael Fraiteur28-Aug-08 22:02
memberGael Fraiteur28-Aug-08 22:02 
GeneralFantastic & Cacheable function attribute Pin
Tom Janssens7-Dec-07 4:40
memberTom Janssens7-Dec-07 4:40 
Hey Gael,
First of all, I think you delivered an EXCELLENT library.

I just discovered this a few hours ago, and I allready wrote a small but very helpfull attribute : a attribute to cache constant but timeconsuming function calls.
If you have a function that takes a lot of time to generate, but the result set is actually the same each time for a certain parameter set, you simply need to define it as follows :
(code is written in VB since we're using vb @ work here)
<br />
Module Module1<br />
  Sub Main()<br />
    Console.WriteLine("calling Div2 for i = 1", DoDiv2(1))<br />
    Console.WriteLine("calling Div2 for i = 2", DoDiv2(2))<br />
    Console.WriteLine("calling Div2 for i = 1", DoDiv2(1))<br />
    Console.ReadLine()<br />
      End Sub<br />
<br /><br />
  <Cacheable()> _<br />
  Function DoDiv2(ByVal i As Integer) As Double<br />
            Console.WriteLine("running Dodiv2 for " + i.ToString())<br />
            Return i / 2<br />
  End Function<br />
End Module<br />

If you run this code, you will see that the function only gets executed once for parameter value 1; the second time it just searches the cached dictionary. This avoids having the same code all over the project caching calculated values !!! It is a huge timesaver and makes you code much more maintainable...

The attribute itself was written like this :
Imports ps = PostSharp.Laos<br />
<br />
<Serializable()> _<br />
<System.AttributeUsage(AttributeTargets.Method, Inherited:=True, AllowMultiple:=False)> _<br />
Public Class CacheableAttribute<br />
  Inherits ps.OnMethodInvocationAspect<br />
<br />
  Shared x As New Hashtable<br />
<br />
  Public Overrides Sub OnInvocation(ByVal pEventArgs As PostSharp.Laos.MethodInvocationEventArgs)<br />
    Dim o() As Object = pEventArgs.GetArguments()<br />
    Dim i As Integer = 0, obj As Object<br />
    For Each obj In o<br />
      i += obj.GetHashCode()<br />
    Next<br />
    If Not x.Contains(i) Then<br />
      x(i) = pEventArgs.Delegate.DynamicInvoke(o)<br />
    End If<br />
    pEventArgs.ReturnValue = x(i)<br />
  End Sub<br />
End Class

And this in such a short time period. Thanks a lot !!!
BTW(Youve got my 5 on both articles)



GeneralRe: Fantastic & Cacheable function attribute Pin
Gael Fraiteur14-Dec-07 12:34
memberGael Fraiteur14-Dec-07 12:34 
GeneralWow -- this is great Pin
Tom Hawkins12-Oct-07 5:43
memberTom Hawkins12-Oct-07 5:43 
GeneralRe: Wow -- this is great Pin
Gael Fraiteur12-Oct-07 6:04
memberGael Fraiteur12-Oct-07 6:04 
GeneralRe: Wow -- this is great Pin
Tom Hawkins12-Oct-07 6:14
memberTom Hawkins12-Oct-07 6:14 
QuestionWhat about properties validation? Pin
Eduard Gomolyako21-Sep-07 3:29
memberEduard Gomolyako21-Sep-07 3:29 
AnswerRe: What about properties validation? Pin
Gael Fraiteur21-Sep-07 3:39
memberGael Fraiteur21-Sep-07 3:39 
GeneralGood Article...But Pin
P.Adityanand20-Sep-07 5:31
memberP.Adityanand20-Sep-07 5:31 
GeneralRe: Good Article...But Pin
Gael Fraiteur20-Sep-07 5:47
memberGael Fraiteur20-Sep-07 5:47 
GeneralRe: Good Article...But Pin
mcory220-Sep-07 6:38
membermcory220-Sep-07 6:38 
GeneralRe: Good Article...But Pin
mintxelas24-Sep-07 21:20
membermintxelas24-Sep-07 21:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web04 | 2.8.190214.1 | Last Updated 20 Sep 2007
Article Copyright 2007 by Gael Fraiteur
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid