Archive

Microsoft PDC 2008 is over, as are American presidential elections. We can now talk about something exiting again and hope it will gain some momentum in the community (thus blogging about boring technical details during PDC was intentional).

It's maybe the most exciting feature of PostSharp after PostSharp itself: aspects are now inheritable.

A code sample worthing a hundred words, here is what is now possible:

public interface IDiary
{
    Contact TryFindContact([NonEmpty] string name);

    [return: NonNull]
    Contact FindContact([NonEmpty] string name);

    void Update([NotNull] Contact contact);
}

 

The aspects (here NonEmpty and NonNull) are applied on methods of an interface... but are effective on all implementations of this interface!

(Oh yes, for this to work correctly, you need to download the latest build from http://download.postsharp.org/builds/1.5 -- the CTP 2 contained some bugs that have been solved in the mean time).

The implementation of these aspects does not use Laos, but a dedicated plug-in that generates optimal MSIL instructions. You can download the plug-in using an SVN client from https://postsharp-user-plugins.googlecode.com/svn/trunk/1.5/Torch.

The inheritance feature is indeed implemented at the level of MulticastAttribute, and not directly in Laos. It means that you can use it inside any plug-in. But let's now see an example using Laos.

Simple Invariant Checking

Here is an interesting first example of this feature; it is both simple and useful.

We want to check invariants, and we want to do it simply. With PostSharp 1.5, checking invariants can be as simple as implementing an interface, say IConsistant.

public interface IConsistant
{
    void CheckConsistency();
}

When an object implements the IConsistant assembly, we want the CheckConsistency method to be "automagically" invoked after each non-private instance method.

It's possible by applying a single custom attribute, say [ConsistantAspect] to the IConsistant interface:

[ConsistantAspect]
public interface IConsistant

This custom attribute is unbelievably simple:

[AttributeUsage(AttributeTargets.Interface)] [MulticastAttributeUsage(MulticastTargets.Method, TargetMemberAttributes = MulticastAttributes.Public | MulticastAttributes.Protected | MulticastAttributes.Internal | MulticastAttributes.Instance, Inheritance = MulticastInheritance.Multicast)] [Serializable] public sealed class ConsistantAspect : OnMethodBoundaryAspect { public override void OnSuccess(MethodExecutionEventArgs eventArgs) { ((IConsistant) eventArgs.Instance).CheckConsistency(); } }

 

Basically, we have create an OnMethodBoundaryAspect and we implement the OnSuccess handler that invokes the CheckConsitency method when the target method has successfully completed. The AttributeUsage custom attribute restricts the use of this aspect to interfaces; actually, we will use it only once: on the IConsistant method.

The interesting part if the custom attribute MulticastAttributeUsage on the top of that:

  • The property TargetMemberAttributes is old and known: here we define that we want to apply the aspect only to non-private instance methods.
  • The property Inheritance is the new and interesting one: the value Multicast means that the aspect should be inherited (from the interface to classes implementing the interface) and then multicast to all methods matching TargetMemberAttributes.

As a result, when we implement the interface and have invariants checked automatically:

class Cashbox : IConsistant
{
    public decimal Balance { get; private set; }

    public void Debit(decimal amount)
    {
        this.Balance -= amount;
    }

    public void Credit(decimal amount)
    {
        this.Balance += amount;
    }

    public void CheckConsistency()
    {
        if ( Balance < 0 )
            throw new Exception("Invalid balance.");
    }
    
}

 

As you can see, no aspect is directly applied on Cashbox or on its methods. Aspects are inherited from IConsistant and then propagated to all public instance methods.

I'll blog more about this feature later.

Happy PostSharping!

-gael

Would I be a perfect .NET developer if I did not blog about Windows Azure? I let the response to your own judgment, but even if you don't agree on that statement read on.

If you try to deploy a PostSharp-enabled assembly into the Development Fabric, you will probably get the following exception:

[SecurityException: Request for the permission of type 'System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.]
   System.Runtime.Serialization.Formatters.Binary.ObjectReader.CheckSecurity(ParseRecord pr) +10107255
   System.Runtime.Serialization.Formatters.Binary.ObjectReader.ParseArray(ParseRecord pr) +155
   System.Runtime.Serialization.Formatters.Binary.ObjectReader.ParseObject(ParseRecord pr) +151
   System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadArray(BinaryHeaderEnum binaryHeaderEnum) +581
   System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run() +283
   System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) +559
   System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) +326
   System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream) +33
   PostSharp.Laos.Serializers.BinaryLaosSerializer.Deserialize(Stream stream) in BinaryLaosSerializer.cs:46
   PostSharp.Laos.LaosSerializer.Deserialize(Assembly assembly, String resourceName) in LaosSerializer.cs:68
   ~PostSharp~Laos~Implementation..cctor() +365

Don't panic, it is not the end of the story.

Clearly, there is a security exception. Looking at the stack trace, you see that the exception occurs in the BinaryFormatter.Deserialize. Indeed, the binary formatter requires full trust and... code running inside Windows Azure is only partially trusted.

I could have written the same for code running inside SQL Server 2005, but it's less hyped: very often, when your code is hosted, it is not granted full trust.

Pluggable Serializers

If you are new to PostSharp, you may wonder why BinaryFormatter is invoked at runtime. In fact, at build time, aspects are instantiated and serialized into a binary stream. This stream is stored in a managed resource in the assembly and deserialized at runtime. That's why the binary formatter is invoked.

Fortunately, PostSharp 1.5 CTP 2 comes with a new feature called pluggable serializers. Before, you had no choice: aspects were always serialized using the BinaryFormatter. It is the best tool for the job for most situations, but, as you can see here with partial-trust scenarios, it is not always possible to use it.

But there are other serializers in .NET, isn't it? So why not to use them? That's exactly the idea behind pluggable serializers: you can now choose the serializer used to to serialize aspects at build time and deserialize them at rutime.

PostSharp 1.5 comes with three serializers: BinaryLaosSerializer (relying on BinaryFormatter), XmlLaosSerializer (relying on XmlSerializer) and StateBagSerializer (requiring manual implementation in all classes, see documentation). All implement the abstract class PostSharp.Laos.Serializer; if you need another serializer, you can develop your.

Once you have selected a serializer, you should tell PostSharp to use it for your aspect. This is done by applying the custom attribute LaosSerializerAttribute to your aspect class. For instance:

[LaosSerializer(typeof(XmlLaosSerializer))]
public class SomeAspect : OnMethodBoundaryAspect
{ 
[XmlElement] public string Name;
public override void OnEntry(MethodExecutionEventArgs eventArgs) { } }

Since this serializer does not require full trust... it simply works in Windows Azure!

Why do I need a serializer, anyway?

Excellent remarks. Serializers are good when there there is some non-trivial state to be stored. But if your aspect is an isolated custom attribute with some public fields or public properties, it is far better to instantiate the attribute at runtime using the values provided to construct the custom attribute.

For instance, if a custom attribute instance is defined by:

[SomeAspect(Name = "Hello")]

It would be much better (faster and byte-wise more compact) to create the aspect using the following code:

SomeAspect instance = new SomeAspect { Name = "Hello" };

This is exactly what happens if you use the custom attribute LaosSerializerAttribute and pass null to the constructor parameter. The aspect won't be serialized; it will be instantiated and initialized at runtime by some auto-generated code. And this is actually the way aspects for Compact Framework and Silverlight work.

So remember: even if you don't write partially trusted code, you can still use the "null serializer" to improve the size and performance of your assemblies.

Happy PostSharping!

-gael

In the first part of this article, I explained that Silverlight (SL) or Compact Framework (CF) assemblies were not loaded in the CLR. However, as you could have noticed in the last sample, user code still receives instances of Type, FieldInfo, or MethodBase. How is this possible, since assemblies are not loaded, even not "for reflection only"?

Well, here, there is some magic. All these classes (Type, FieldInfo, or MethodBase) are actually abstract ones. You are used to runtime implementations (RuntimeType, RuntimeFieldInfo and so on). But in PostSharp, you don't get runtime implementations (fairly enough, since we are at build time!), so you get so-called reflection wrappers.

Reflection wrappers are an emulation of System.Reflection that purely relies on the PostSharp object model.

Once again to be sure: Reflection wrappers look like System.Reflection but it is not System.Reflection.

And now something even more important:

Even if you target the full framework, you receive reflection wrappers instead of runtime objects.

And this is another big difference between 1.0 and 1.5: 1.0 was not very strict about this; sometimes you received reflection wrappers and sometimes (notoriously in CompoundAspect) runtime objects. In version 1.5, you always receive reflection wrappers.

Gotchaes that will get you

There are subtle differences between PostSharp reflection wrappers and native System.Reflection. If you don't understand them, you risk spending hours in debugging.

  • Never compare by reference. That is, never use the equal (==) operator. Unlike native System.Reflection, two instances can represent the same thing. What is more, comparing a wrapper to a native element of System.Reflection never works. So use the Equals method instead of the == operator.
  • Never uses native equality or inheritance comparing methods. So don't use typeof(Guid).Equals(targetType) but targetType.Equals(typeof(Guid)). I know this sucks (equality is supposed to be commutative), but... there is no workaround!

The safest way is to use the class PostSharp.Reflection.ReflectionTypeComparer for all comparisons... or when you build a dictionary.

Getting the underlying runtime object

That being said, unless you target SL or CF, you can always retrieve the "usual" runtime/native object and work, well, as usually.

First, let me give you a good reason not to do it: doing so would load the type in the CLR, which would have some performance penalty, and may sometimes not succeed at all (for instance if you have an unresolved extern method -- unresolved because you want to implement them using PostSharp, for instance). It seems that static constructors are not invoked just because of loading the Type object, and I hope (but am not sure) that methods are not JIT compiled. So the reason is maybe good but arguably small, I have to admit.

So if you don't care about the eventual performance penalty, go for it: the secret is to retrieve the property Type.UnderlyingSystemType, FieldInfo.UnderlyingSystemField or MethodBase.UnderlyingSystemMethod. You will get a real-native-runtime-system object.

Since even PostSharp 1.5 loads assemblies in the CLR (when not targeting SL or CF), the difference is maybe slight. But future versions of PostSharp may avoid loading assemblies in the CLR unless strictly necessary. And by requiring the underlying runtime element, you make it necessary... maybe unnecessarily.

Getting the assembly

A problem with reflection wrappers is that Assembly is a sealed class; we cannot override it. So you cannot get a transparent assembly wrapper. So instead of getting an Assembly (typically in a CompoundAspect applied at assembly level), you will get an IAssemblyWrapper. And, yes, you can use IAssemblyWrapper.UnderlyingSystemAssembly if you like.

Conclusion

Pay attention: reflection wrappers are not 'usual' System.Reflection objects!