What really changed between 1.0 and 1.5 #2: Understanding reflection wrappers

by Gael Fraiteur on 28 Oct 2008

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!