PostSharp 4.3 / / Post­Sharp Documentation / Custom Patterns / Developing Custom Aspects / Developing Simple Aspects / Intercepting Properties and Fields
Intercepting Properties and Fields

In .NET, both fields and properties are "things" that can be set and get. You can intercept get and set operations using the LocationInterceptionAspect. It makes it possible to develop useful aspects, such as validation, filtering, change tracking, change notification, or property virtualization (where the property is backed by a registry value, for instance).

This topic contains the following sections:

Intercepting Get operations

In this example, we will see how to create an aspect that filters the value read from a field or property.

To create an aspect that filters the value read from a field or property

  1. Create an aspect that inherits from LocationInterceptionAspect and add the custom attribute [PSerializableAttribute].

  2. Override the OnGetValue(LocationInterceptionArgs) method.

    C#
                                  [PSerializable] 
                                  
    
                                  public
                                   
                                  class
                                   StringCheckerAttribute : LocationInterceptionAspect 
    { 
        
                                  public
                                   
                                  override
                                   
                                  void
                                   OnGetValue(LocationInterceptionArgs args) 
        { 
            
                                  base
                                  .OnGetValue(args); 
        } 
    
    }
                                
  3. Calling base.OnGetValue actually retrieves the value from the underlying field or property, and populates the Value property. Add some code to check if the property currently is set to null If the current value is null, we want to return a predefined value. To do this we can set the Value property. Any time this property is requested, and it is set to null, the value "foo" will be returned.

    C#
                                   
                                  public
                                   
                                  override
                                   
                                  void
                                   OnGetValue(LocationInterceptionArgs args) 
    { 
        
                                  base
                                  .OnGetValue(args); 
    
        
                                  if
                                   (args.Value == 
                                  null
                                  ) 
        { 
            args.Value = 
                                  "foo"
                                  ; 
        } 
    }
                                
  4. Now that you have a complete getter interception aspect written you can attach it to the target code. Simply add an attribute to either properties or fields to have the interception attached.

    C#
                                   
                                  public
                                   
                                  class
                                   Customer 
    { 
        [StringChecker] 
        
                                  private
                                   
                                  readonly
                                   
                                  string
                                   _address; 
    
        
                                  public
                                   Customer(
                                  string
                                   address) 
        { 
            _address = address; 
        } 
        [StringChecker] 
        
                                  public
                                   
                                  string
                                   Name { 
                                  get
                                  ; 
                                  set
                                  ; } 
        
                                  public
                                   
                                  string
                                   Address { 
                                  get
                                   { 
                                  return
                                   _address; } } 
    }
                                
    Note Note

    Adding aspects to target code one property or field at a time can be a tedious process. There are a number of techniques in the article Adding Aspects to Multiple Declarations that explain how to add aspects en mass.

  5. Now when you create an instance of a customer and immediately try to access the Name and Address values the get request will be intercepted and null values will be returned as "foo".

    C#
                                   
                                  class
                                   Program 
    { 
        
                                  static
                                   
                                  void
                                   Main(
                                  string
                                  [] args) 
        { 
            
                                  var
                                   customer = 
                                  new
                                   Customer(
                                  "123 Main Street"
                                  ); 
            Console.WriteLine(
                                  "Address: {0}"
                                  , customer.Address); 
            Console.WriteLine(
                                  "Name: {0}"
                                  , customer.Name); 
            Console.ReadKey(); 
        } 
    }
                                
    Intercepting Properties 1

Property and field interception is a simple and seamless task. Once you have intercepted your target you can act on the target or you can allow the original code to execute.

Intercepting Set operations

The previous section showed how to intercept a get accessor. Intercepting a set accessor is accomplished in a similar manner by implementing OnSetValue(LocationInterceptionArgs) in the LocationInterceptionAspect.

The following snippet shows the addition of OnSetValue(LocationInterceptionArgs) to the StringCheckerAttribute example:

C#
                        [PSerializable] 
                        

                        public
                         
                        class
                         StringCheckerAttribute : LocationInterceptionAspect 
{ 
                        

                        public
                         
                        override
                         
                        void
                         OnGetValue(LocationInterceptionArgs args) 
{ 
                        

                        base
                        .OnGetValue(args); 
} 
                        


                        public
                         
                        override
                         
                        void
                         OnSetValue(LocationInterceptionArgs args) 
{ 
                        

                        base
                        .OnSetValue(args); 
} 
}
                      

When applied to a property with a set operator, OnSetValue(LocationInterceptionArgs) will intercept the set operation. In the Customer example shown below, OnSetValue(LocationInterceptionArgs) will be called whenever the Name property is set:

C#
                         
                        public
                         
                        class
                         Customer 
{ 
    .
    .
    .
[StringChecker]
                        

                        public
                         
                        string
                         Name { 
                        get
                        ; 
                        set
                        ; } 
}
                      

The SetNewValue(Object) method of LocationInterceptionArgs can be used instead of base.OnSetValue() to pass a different value in for the property. For example, OnSetValue(LocationInterceptionArgs) could be used to check for a null string, and then change the string to a non-null value:

C#
                        [PSerializable] 
                        

                        public
                         
                        class
                         StringCheckerAttribute : LocationInterceptionAspect 
{ 
    .
    .
    .
                        

                        public
                         
                        override
                         
                        void
                         OnSetValue(LocationInterceptionArgs args) 
{ 
                        

                        if
                         (args.Value == 
                        null
                        )
{
args.Value = “Empty String";
} 


args.ProceedSetValue();

} 
}
                      
Getting and setting the underlying property

PostSharp provides a mechanism to check a property’s underlying value via the LocationInterceptionArgs.GetCurrentValue() method. This can be useful to check the current property value when a setter is called and then take some appropriate action.

For example, the following snippet shows a modified OnSetValue(LocationInterceptionArgs) method which gets the current underlying property value and compares the (new) value passed into the setter against the current value. If current and new value don’t match then some message is written:

C#
                         
                        public
                         
                        override
                         
                        void
                         OnSetValue(LocationInterceptionArgs args)
{
  
                        //get the current underlying value
                        

                          
                        string
                         existingValue = (
                        string
                        )args.GetCurrentValue();

  
                        if
                         (((existingValue==
                        null
                        ) && (args.Value != 
                        null
                        )) || (!existingValue.Equals(args.Value)))
  {
    Console.WriteLine(“Value changed.”);
    args.ProceedSetValue();
  }
}
                      
Note Note

GetCurrentValue() will call the underlying property getter without going through OnGetValue(LocationInterceptionArgs). If several aspects are applied to the property (and/or to the property setter), GetCurrentValue() will go through the next aspect in the chain of invocation.

PostSharp also provides a mechanism to set the underlying property in a getter via the SetNewValue(Object) method of LocationInterceptionArgs. This could be used for example, to ensure that a default value is assigned to the underlying property if there is currently no value. The following snippet shows a modified OnGetValue(LocationInterceptionArgs) method which gets the current underlying value, and sets a default value if the current value is null:

C#
                         
                        public
                         
                        override
                         
                        void
                         OnGetValue(LocationInterceptionArgs args)
{
    
                        object
                         o = args.GetCurrentValue();
    
                        if
                         (o == 
                        null
                        )
    {
        args.SetNewValue(
                        "value not set"
                        );
    }

    
                        base
                        .OnGetValue(args);
}
                      
Intercepting fields

One benefit to implementing a LocationInterceptionAspect is that it can be applied directly to fields, allowing for reads and writes to those fields to be intercepted, just like with properties.

Applying a LocationInterceptionAspect implementation to a field is simply a matter of setting it as an attribute on a field, just as it was done with a property:

C#
                         
                        public
                         
                        class
                         Customer 
{ 
    .
    .
    .
[StringChecker]
                        

                        public
                         
                        string
                         name; 
}
                      

With the attribute applied to the name field, all attempts to get and set that field will be intercepted by StringChecker in its OnGetValue(LocationInterceptionArgs) and OnSetValue(LocationInterceptionArgs) methods.

Note that when a LocationInterceptionAspect is added to a field, the field is replaced by a property of the same field and visibility. The field itself is renamed and made private.

Getting the property or property being accessed

Information about the property or field being intercepted can be obtained through the LocationInterceptionArgs via its Location property. The type of this property, LocationInfo, can represent a FieldInfo, a PropertyInfo, or a ParameterInfo (although LocationInterceptionAspect cannot be added to parameters).

One use for this is to reflect the property name whenever a property is changed. In the following example, we have an Entity class that implements INotifyPropertyChanged and a public OnPropertyChanged method which allows notifications to be made whenever a property is changed. The Customer class has been modified to derive from Entity.

C#
                         
                        class
                         Entity : INotifyPropertyChanged
{

  
                        public
                         
                        event
                         PropertyChangedEventHandler PropertyChanged;

  
                        public
                         
                        void
                         OnPropertyChanged(
                        string
                         propertyName)
  {
    
                        if
                         (PropertyChanged != 
                        null
                        )
     PropertyChanged(
                        this
                        , 
                        new
                         PropertyChangedEventArgs(propertyName));
  }
}

 
                        class
                         Customer : Entity
{
  
                        public
                         
                        string
                         Name { 
                        get
                        ; 
                        set
                        ; }
}
                      

With the ability to invoke an OnPropertyChanged event, we can create a LocationInterceptionAspect which invokes this event when setting a value and pass in the property name from the underlying PropertyInfo object:

C#
                        [PSerializable] 
                        

                        public
                         
                        class
                         NotifyPropertyChangedAttribute : LocationInterceptionAspect 
{ 
                        


                        public
                         
                        override
                         
                        void
                         OnSetValue(LocationInterceptionArgs args) 
{ 
If ( args.Value != args.GetCurrentValue() )
{
args.Value = args.Value;
args.ProceedSetValue();
((Entity)args.Instance).OnPropertyChanged(args.Location.Name);
} 
} 
}
                      
Note Note

This example is a simplistic implementation of the NotifyPropertyChangedAttribute aspect. For a production-ready implementation, see the section INotifyPropertyChanged.

This aspect can then be applied to the Customer class:

                        [NotifyPropertyChangedAttribute]
                        

                        class
                         Customer : INotifyPropertyChanged
{
    
                        public
                         
                        string
                         Name { 
                        get
                        ; 
                        set
                        ; }
}
                      

Now when the Name property is changed, NotifyPropertyChangedAttribute will invoke the Entity.OnPropertyChanged method passing in the property name retrieved from its underlying property.

See Also