Declarative Security and Reflection
If you’re using the CustomAttributeData APIs to examine declarative security permission, you might notice that the returned information looks a little strange. The CustomAttributeData object that you’ll see for the declarative security attribute will have a ConstructorInfo that is of the correct type, but may not be the same constructor that was actually used in the source code. Similarly, the ConstructorArguments and NamedArguments property will say that the attribute has neither, even though if you look at your source code you may see one or both of these types of arguments.
So, what’s going on?
This behavior is a side-effect of the fact that security permission attributes aren’t “real” custom attributes in the sense that they’re not stored in the CustomAttribute metadata table, but rather in their own table – the DeclSecurity table. Reflection tries to abstract this away from the callers of the CustomAttributeData APIs by including both real custom attributes and pseudo-custom attributes like declarative security in its return value. However, it cannot abstract this detail away perfectly, which is why the constructor information that it normally provides for custom attributes is not provided for declarative security.
Specifically, the metadata format for declarative security blobs is structured in such a way that it is easy for the security system to quickly and lazily determine if a permission blob is needed during execution, and if so very quickly rehydrate the permission in question. For instance, the format of the declarative security table allows us to avoid doing any work at all when a method has a declarative assert until the point that a demand actually hits the stack frame with that assert. This optimization means that in full trust, you will never have to waste time building up permission objects that might never be used. Similarly, the blog format is optimized to make it quick for us to build up that asserted permission set once a demand does hit the method in question – meaning that we don’t slow down stack walks any more than necessary.
Since the metadata format is optimized for this scenario, it’s not setup in such a way that you can trivially map it back to the exact constructor that might have been used in code in the first place. In fact, in the general case, it’s actually impossible to do so. If you really did want to reverse-engineer what the source declaration might have been for declarative security you could do so by reading out of the declarative security table, and having special knowledge about how each different type of permission can build up its internal state.
(Interestingly, you can also see this separation of declarative security from real custom attributes in the IL syntax. Rather than using a .custom declaration for declarative security, as you would for a standard custom attribute, you use either a .permission or a .permissionset declaration. ILDASM will show this if you look at the IL output of an assembly that uses declarative security).