Skip to content

Latest commit

 

History

History
193 lines (166 loc) · 8.53 KB

vi.b.3-custom-attributes.md

File metadata and controls

193 lines (166 loc) · 8.53 KB

VI.B.3 Custom attributes

[Example:

This subclause includes many example uses of custom attributes to help clarify the grammar and rules described above. The examples are written in C#, and each one shows a collection of one or more attributes, applied to a class (called "App"). The hex and 'translation' of the custom attribute blobs are shown as comments. The following abbreviations are used:

  • FIELD = ELEMENT_TYPE_FIELD
  • PROPERTY = 0x54
  • STRING = ELEMENT_TYPE_STRING
  • SZARRAY = ELEMENT_TYPE_SZARRAY
  • U1 = ELEMENT_TYPE_U1
  • I4 = ELEMENT_TYPE_I4
  • OBJECT = 0x51
 // ********************************************************************************
 // CustomSimple.cs
 using System;
 [AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]
 class B : Attribute { public B(int i, ushort u) {} }

 [B(7,9)]    // 01 00          // Prolog
             // 07 00 00 00    // 0x00000007
             // 09 00          // 0x0009
             // 00 00          // NumNamed
 class App { static void Main() {} }

 // ********************************************************************************
 // CustomString.cs
 using System;
 [AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]
 class A : Attribute {
    public  string field;        // field
    private string back;         // backing field for property
    public  string prop {        // property
       get { return back;  }
       set { back = value; }
    }
    public  A(string x) {}       // ctor
 }

 [A(null)]   // 01 00           // Prolog
             // FF              // null
             // 00 00           // NumNamed

 [A("")]     // 01 00           // Prolog
             // 00              // zero-length string
             // 00 00           // NumNamed

 [A("ab",field="cd",prop="123")]  // 01 00             // Prolog
                                  // 02 61 62          // "ab"
                                  // 02 00             // NumNamed
                                  // 53 0e             // FIELD, STRING
                                  // 05 66 69 65 6c 64 // "field" as counted-UTF8
                                  // 02 63 64          // "cd" as counted-UTF8
                                  // 54 0e             // PROPERTY, STRING
                                  // 04 70 72 6f 70    // "prop" as counted-UTF8
                                  // 03 31 32 33       // "123" as counted-UTF8
 class App { static void Main() {} }

 // ********************************************************************************
 // CustomType.cs
 using System;
 [AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]
 class C : Attribute {
    public C(Type t) {}
 }

 [C(typeof(C))]
 // 01 00                                              // Prolog
 // 01 43                                              // "C" as counted-UTF8
 // 00 00                                              // NumNamed

 [C(typeof(string))]
 // 01 00                                              // Prolog
 // 0d 53 79 73 74 65 6d 2e 53 74 72 69 6e 67          // "System.String" as counted-UTF8
 // 00 00                                              // NumNamed

 [C(typeof(System.Windows.Forms.Button))]
 // 01 00                                              // Prolog
 // 76 53 79 73 74 65 6d 2e 57 69 6e 64 6f 77          // "System.Window
 // 73 2e 46 6f 72 6d 73 2e 42 75 74 74 6f 6e 2c 53    // s.Forms.Button,S
 // 79 73 74 65 6d 2e 57 69 6e 64 6f 77 73 2e 46 6f    // ystem.Windows.Fo
 // 72 6d 73 2c 20 56 65 72 73 69 6f 6e 3d 32 2e 30    // rms, Version=2.0
 // 2e 33 36 30 30 2e 30 2c 20 43 75 6c 74 75 72 65    // .3600.0, Culture
 // 3d 6e 65 75 74 72 61 6c 2c 20 50 75 62 6c 69 63    // =neutral, Public
 // 4b 65 79 54 6f 6b 65 6e 3d 62 37 37 61 35 63 35    // KeyToken=b77a5c5
 // 36 31 39 33 34 65 30 38 39 00 00                   // 61934e089"
 // 00 00                                              // NumNamed
 class App { static void Main() {} }

Notice how various types are 'stringified': if the type is defined in the local assembly, or in mscorlib, then only its full name is required; if the type is defined in a different assembly, then its fully-qualified assembly name is required, includeing Version, Culture and PublicKeyToken, if non-defaulted.

// ********************************************************************************
// CustomByteArray.cs
using System;
class D : Attribute {
   public  byte[] field;                             // field
   private byte[] back;                              // backing field for property
   public  byte[] prop {                             // property
      get { return back;  }
      set { back = value; }
   }
   public D(params byte[] bs) {}                     // ctor
}

[D(1,2, field=new byte[]{3,4},prop=new byte[]{5})]
// 01 00                                             // Prolog
// 02 00 00 00                                       // NumElem
// 01 02                                             // 1,2
// 02 00                                             // NumNamed
// 53 1d 05                                          // FIELD, SZARRAY, U1
// 05 66 69 65 6c 64                                 // "field" as counted-UTF8
// 02 00 00 00                                       // NumElem = 0x00000002
// 03 04                                             // 3,4
// 54 1d 05                                          // PROPERTY, SZARRAY, U1
// 04 70 72 6f 70                                    // "prop" as counted-UTF8
// 01 00 00 00                                       // NumElem = 0x00000001
// 05                                                // 5
class App { static void Main() {} }

// ********************************************************************************
// CustomBoxedValuetype.cs
using System;
[AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]
class E : Attribute {
   public object obj;                           // field called "obj"
   public object o {                            // property called "o"
      get { return o; }
      set { o = value; }
   }
   public E() {}                                // default ctor
   public E(object x) {}
}

[E(42)]                                         // boxed 42
// 01 00                                        // Prolog
// 08                                           // I4
// 2a 00 00 00                                  // 0x0000002A
// 00 00                                        // NumNamed

[E(obj=7)]                                      // named field
// 01 00                                        // Prolog
// 01 00                                        // NumNamed
// 53 51                                        // FIELD, OBJECT
// 03 6f 62 6a                                  // "obj" as counted-UTF8
// 08                                           // I4
// 07 00 00 00                                  // 0x00000007

[E(o=0xEE)]                                     // named property
// 01 00                                        // Prolog
// 01 00                                        // NumNamed
// 54 51                                        // PROPERTY, OBJECT
// 01 6f                                        // "o" as counted-UTF8
// 08                                           // I4
// ee 00 00 00                                  // 0x000000EE
class App { static void Main() {} }

This example illustrates how to construct blobs for a custom attribute that accepts a System.Object in its constructor, as a named field, and as a named property. In each case, the argument given is an int32, which is boxed automatically by the C# compiler.

Notice the OBJECT = 0x51 byte. This is emitted for "named" fields or properties of type System.Object.

// ********************************************************************************
// CustomShortArray.cs
using System;
[AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]
class F : Attribute {
    public F(params short[] cs) {}    // ctor
}

[F()]
// 01 00                            // Prolog
// 00 00 00 00                      // NumElem
// 00 00                            // NumNamed

[F(null)]
// 01 00                            // Prolog
// ff ff ff ff                      // NumElem = -1 => null
// 00 00                            // NumNamed

[F(1,2)]
// 01 00                            // Prolog
// 02 00 00 00                      // NumElem
// 01 00 02 00                      // 0x0001, 0x0002
// 00 00                            // NumNamed
class App { static void Main() {} }

end example]