Udostępnij za pośrednictwem


Pseudo custom attributes

Tomorrow I'd like to talk about the PreserveSig pseudo custom attribute, but first I thought I'd briefly point out the difference between pseudo custom attributes and "real" custom attributes.

The CLR/.NET Framework has a handful of pseudo custom attributes that are used just like real custom attributes in high-level source code, but are not stored in metadata the same way that custom attributes are.  Each pseudo custom attribute is represented with unique bits in metadata, which happens to be more efficient than a general-purpose custom attribute.

Ideally, you wouldn't have to know whether a custom attribute is a pseudo custom attribute unless you were doing low-level metadata processing.  Unfortunately, the illusion that pseudo custom attributes are just like real custom attributes breaks down when you use reflection (at least in versions 1.0 and 1.1 of the .NET Framework).  The GetCustomAttributes APIs do not "see" pseudo custom attributes, although other APIs can be used to determine whether specific pseudo custom attributes are present.

The following C# class is marked with a pseudo custom attribute (SerializableAttribute) and a real custom attribute (ObsoleteAttribute):

  using System;

  [Serializable, Obsolete]

  public class C

  {

    ...

  }

We can see the difference between the two by viewing the compiled class using the IL Disassembler (ILDASM.EXE):

  .class public auto ansi serializable

    beforefieldinit C

  extends [mscorlib]System.Object

  {

  .custom instance void

      [mscorlib]System.ObsoleteAttribute::.ctor()

      = ( 01 00 00 00 )

  ...

  } // end of class C

If you use reflection to get the custom attributes on C:

  Type t = typeof(C);

  object [] attributes = t.GetCustomAttributes(false);

  foreach (object o in attributes)

    Console.WriteLine(o);

you get only the single real custom attribute returned:

  System.ObsoleteAttribute

But you could find out that C is marked with SerializableAttribute by checking the value of the Type.IsSerializable property.  For some pseudo custom attributes, like MarshalAsAttribute, there simply is no way to get all the attribute's information without using the metadata APIs.

Reflection emit hides the differences between pseudo custom attributes and real custom attributes by letting you use the attribute classes when emitting either kind of attribute.  However, pseudo custom attributes tend to get special treatment in the reflection emit APIs.  For example, TypeBuilder and ModuleBuilder have a DefinePInvokeMethod API that emits the DllImportAttribute bits for you (although it's not general enough to express everything that the attribute can).

Unlike custom attributes, pseudo custom attributes are not used for extensibility - there's a predefined list:

System.NonSerializedAttribute
System.SerializableAttribute
System.Runtime.CompilerServices.MethodImplAttribute
System.Runtime.InteropServices.ComImportAttribute
System.Runtime.InteropServices.DllImportAttribute
System.Runtime.InteropServices.FieldOffsetAttribute
System.Runtime.InteropServices.InAttribute
System.Runtime.InteropServices.MarshalAsAttribute
System.Runtime.InteropServices.OptionalAttribute
System.Runtime.InteropServices.OutAttribute
System.Runtime.InteropServices.PreserveSigAttribute
System.Runtime.InteropServices.StructLayoutAttribute

The following attributes technically aren't pseudo custom attributes from the CLR perspective, but exhibit the same behavior:

System.Reflection.AssemblyAlgorithmIdAttribute
System.Reflection.AssemblyCultureAttribute
System.Reflection.AssemblyFlagsAttribute
System.Reflection.AssemblyVersionAttribute

That's because compilers like Visual C# and VB.NET emit specific metadata when these are used, rather than emitting the custom attributes themselves.  (I guess that makes them pseudo pseudo custom attributes?)

There are also security attributes, such as the attributes in the System.Security.Permissions namespace, which are serialized into metadata as XML.  Although these are not pseudo custom attributes, they can't be retrieved via reflection due to their distinct metadata representation.

Finally, I want to share an interesting fact about the IL Assembler (ILASM.EXE) that I didn't know until recently.  Whenever I've written pseudo custom attributes in IL Assembler, I've used the syntax that ILDASM produces.  But like C#, VB.NET, and other .NET languages, ILASM allows you to use regular custom attribute syntax with pseudo custom attributes!  If you compile the following with ILASM:

  .class public auto ansi beforefieldinit C

  extends [mscorlib]System.Object

  {

  .custom instance void

      [mscorlib]System.ObsoleteAttribute::.ctor()

      = ( 01 00 00 00 )

    .custom instance void [mscorlib]

      System.SerializableAttribute::.ctor()

      = ( 01 00 00 00 )

  ...

  } // end of class C

then view the result with ILDASM, you'll see the same metadata shown earlier when disassembling the C# code.  The SerializableAttribute, when compiled, becomes the serializable bit in metadata.

Comments

  • Anonymous
    July 27, 2004
    The comment has been removed
  • Anonymous
    January 17, 2005
    ObfuscationAttribute
  • Anonymous
    February 20, 2008
    大家都知道AssemblyVersionAttribute是用来指定Assembly的版本号使用的,但是不知道你有没有考虑过这个问题:这个Attribute真的生成到了最后的Assembly中吗?
  • Anonymous
    February 20, 2008
    大家都知道 AssemblyVersionAttribute 是用来指定 Assembly 的版本号使用的,但是不知道你有没有考虑过这个问题:这个 Attribute 真的生成到了最后的 Assembly
  • Anonymous
    February 20, 2008
    大家都知道 AssemblyVersionAttribute 是用来指定 Assembly 的版本号使用的,但是不知道你有没有考虑过这个问题:这个 Attribute 真的生成到了最后的 Assembly