Not all Custom Attributes are created equal

Consider the following code:


public class MyAttribute : Attribute
{
  public MyAttribute(UnmanagedType foo)
  {
  }

  public int Bar { get; set; }
}

[StructLayout(LayoutKind.Sequential)]
public struct Test
{
  [CLSCompliant(false)]
  [MyAttribute(UnmanagedType.ByValArray, Bar = 4)]
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  public ushort[] ArrayShorts;
}

class Program
{
  static void Main(string[] args)
  {
    FieldInfo field_info = typeof(Test).GetField(“ArrayShorts”);
    object[] custom_attributes = field_info.GetCustomAttributes(typeof(MarshalAsAttribute), false);
    Debug.WriteLine(“Attributes: “ + custom_attributes.Length.ToString());
    custom_attributes = field_info.GetCustomAttributes(typeof(MyAttribute), false);
    Debug.WriteLine(“Attributes: “ + custom_attributes.Length.ToString());
    custom_attributes = field_info.GetCustomAttributes(typeof(CLSCompliantAttribute), false);
    Debug.WriteLine(“Attributes: “ + custom_attributes.Length.ToString());
  }
}


The code defines a custom attribute, then defices a struct that uses that attribute, along with some BCL-provided attributes.  It then uses reflection to get back those attributes. What would you expect the output to be?  Probably this:


Attributes: 1


Attributes: 1


Attributes: 1




And if you run it against the full framework, that’s exactly what you get.  But if you run it against the Compact framework, you instead get this:


Attributes: 0


Attributes: 1


Attributes: 1


Yes, you’re seeing it correctly, the MarshalAsAttribute doesn’t show up.  So immediately I call this a bug since the frameworks differ and there is no documentation that says they should (even if it were documented I’d call it a bug).  So I did a little asking around and a little research.  It turns out that there is a difference between MarshalAs and the other attributes.  According to the ECMA-335 spec for the Common Language Interface, section 21.2



There are two kinds of custom attributes, called genuine custom attributes, and pseudo custom attributes.


Custom attributes and pseudo custom attributes are treated differently, at the time they are defined, as follows:




  • A custom attribute is stored directly into the metadata; the‘blob’ which holds its defining data is stored as-is. That ‘blob’ can be retrieved later.



  • A pseudo custom attribute is recognized because its name is one of a short list. Rather than store its ‘blob’ directly in metadata, that ‘blob’ is parsed, and the information it contains is used to set bits and/or fields within metadata tables. The ‘blob’ is then discarded; it cannot be retrieved later.

The spec goes on to say that the MarshalAsAttribute is a pseudo custom attribute, so it would fall into the second bullet above.  If you re-read the last sentence in that bullet  you’ll see that “it cannot be retrieved later.”  So, in fact, it seems like the full framework is the one in error here, at least per the spec! This attribute should not be readable at all at run time.


Now why the authors of the spec would have made such a strange exception is beyond me.  THe method in the language is clearly called GetCustomattributes, not “GetOnlySomeCustomAttributes” and this is the first I’ve even heard of pseudo cutom attributes, which tells me it’s not well documented and likely not well known.  So while the Compact Framework follows the spec to the letter, if you go with reasonable expectations as a guideline, I’m going to have to say that it’s behavior is wrong and that the spec is incorrect and needs revising.

One Comment

  1. Gotta love stuff like this. It’s interesting how roads like this lead to finding out really odd stuff about your platform. I remember a while back having an issue with Vista, DateTimePickers and Toolstrips, that only taught me how much of a hack punching the datetimepicker overlay onto the screen appears to be.

    Thanks for the write-up on this one, cool read. :)

Leave a Reply