Quick Examples

This section shows a few examples to demonstrate what PostSharp is about:

Standard patterns

PostSharp provides implementations of some of the patterns that are the most commonly found in .NET code bases:

Example

The following code snippet illustrates an object model where INotifyPropertyChanged, undo/redo, code contracts, aggregation and code contracts are all implemented using PostSharp ready-made attributes.

C#
[NotifyPropertyChanged]
public class CustomerViewModel
{
   [Required]
   public Customer Customer { get; set; }

   public string FullName { get { return this.Customer.FirstName + " " + this.Customer.LastName; } }
}

[NotifyPropertyChanged]
[Recordable]
public class Customer
{
   public string FirstName { get; set; }
   public string LastName { get; set; }

   [Child]
   public AdvisableCollection<Address> Addresses { get; set; }

   [Url]
   public string HomePage { get; set; }

   [Log]
   public void Save(DbConnection connection)
   {
      // ...
   }
}

[NotifyPropertyChanged]
[Recordable]
public class Address
{
   [Parent]
   public Customer Parent { get; private set; }

   public string Line1 { get; set; }
}
Thread safety patterns

Multi-threading is a great demonstration of the limitations of conventional object-oriented programming. Thread synchronization is traditionally addressed at an absurdly low level of abstraction, resulting in excessive complexity and defects.

Yet, several design patterns exist to bring down the complexity of multi-threading. New programming languages have been designed around these patterns: for instance Erlang over the Actor pattern and functional programming over the Immutable pattern.

PostSharp gives you the benefits of threading design patterns without leaving C# or VB.

PostSharp supports the following threading models and features:

Example

The following code snippet shows how a data transfer object can be made freezable, recursively but easily:

C#
[Freezable]
public class Customer
{
   public string Name { get; set; }

   [Child]
   public AdvisableCollection<Address> Addresses { get; set; }

}

[Freezable]
public class Address
{
   [Parent]
   public Customer Parent { get; private set; }

   public string Line1 { get; set; }
}

public class Program
{
   public static void Main()
   {
      Customer customer = ReadCustomer( "http://customers.org/11234" );

      // Prevent changes.
      ((IFreezable)customer).Freeze();

      // The following line will cause an ObjectReadOnlyException.
      customer.Addresses[0].Line1 = "Here";
   }
}
Implementation of custom patterns

The attributes that implement the standard and thread safety patterns are called aspects. This terms comes from the paradigm of aspect-oriented programming (AOP). An aspect is a class that encapsulates behaviors that are injected into another class, method, field, property or event. The process of injecting an aspect into another piece of code is called weaving. PostSharp weaves aspects at build time; it is also named a build-time aspect weaver.

PostSharp Aspect Framework is a pragmatic implementation of AOP concepts. All ready-made implementations of patterns are built using PostSharp Aspect Framework. You can use the same technology to automate the implementation of your own patterns.

To learn more about developing your own aspects, see Developing Custom Aspects.

Example

The following code snippet shows a simple [PrintException] aspect that writes an exception message to the console before rethrowing it:

C#
[PSerializable]
class PrintExceptionAttribute : OnExceptionAspect
{
    public override void OnException(MethodExecutionArgs args)
    {
        Console.WriteLine(args.Exception.Message);
    }
}

In the next snippet, the [PrintException] aspect is applied to a method:

C#
class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    [PrintException]
    public void Store(string path)
    {
        File.WriteAllText( path, string.Format( "{0} {1}", this.FirstName, this.LastName ) );
    }
}
Validation of custom patterns

Not all patterns can be fully implemented by the compiler. Many patterns involve a lot of hand-written code. However, they are still patterns because we want to follow the same conventions and approach when solving the same problem. In this case, we have to validate the code against implementation guidelines of the pattern. This is typically achieved during code reviews, but as any algorithmic work, it can be partially automated using the right tool. This is the job of the PostSharp Architecture Framework.

PostSharp Architecture Framework also contains pre-built architectural constraints that help solving common design problems. For instance, the InternalImplementAttribute constraint prevents an interface to be implemented in an external assembly.

See Validating Architecture for more details about architecture validation.

Example

Consider a form-processing application. There may be hundreds of forms, and each form can have dozens of business rules. In order to reduce complexity, the team decides that all business rules will respect the same pattern. The team decides that each class representing a business rule must contain a public nested class named Factory, and that this class must have an [Export(IBusinessRuleFactory)] custom attribute and a default public constructor. The team wants all developers to follow the convention. Therefore, the team decide to create an architectural constraint that will validate the code against the project-specific Business Rule Factory pattern.

C#
[MulticastAttributeUsage(MulticastTargets.Class, Inheritance = MulticastInheritance.Strict)] 
public class BusinessRulePatternValidation : ScalarConstraint 
{ 
    public override void ValidateCode(object target) 
    { 
        var targetType = (Type)target; 

        if (targetType.GetNestedType("Factory") == null) 
        { 
            Message.Write( targetType, SeverityType.Error,  "2001", 
                           "The {0} type does not have a nested type named 'Factory'.", 
                           targetType.DeclaringType,  targetType.Name ); 
        } 

        // ...
    } 
}

[BusinessRulePatternValidation]
public abstract BusinessRule
{
  // ...
}