Standard
ECMA-372
C++/CLI 言語仕様書
西暦2005年12月 初版
ナビゲーション リンクのスキップ

34.8 ネイティブ・クラス

 ネイティブ・クラスは(ネイティブ・クラスは値クラスではないのですが)対応した名前と視認性(§34.6.3 )を持った値クラスとして発行されるべきです。ネイティブ・クラスは次のようにマークされるべきです。
  • "Marshal string" 属性 ansi(§34.7.3 )と
  • "Type layout" 属性 sequential(§34.7.3

 しかしながら、対応する属性、名前空間 System::Runtime::InteropServices からの StructLayoutAttribute(と FieldOffsetAttribute )はソースコード・レベルでネイティブ・クラスに適用することはできません。
 ネスト化ネイティブ・クラスや値クラスは、適切なアクセス可能性を従えて、nested マークが付けられるべきであり、それがネストされている型の内側で定義されるべきです。
 値クラスと同様に、ネイティブ・クラスは [mscorlib]System::ValueType を継承( extends )するべきです。
 ネイティブ・クラスのエンコードに使われる値クラスは、その値が実装によって決定される .size 指定子を、サイズはそのクラスのインスタンスを示すのに必要とされるので、明示的に含むべきです。
 その型のエンコードに使われる値クラスは、System::Runtime::CompilerServices 名前空間の NativeCppClass(§33.2.1 )属性をアタッチするべきです。
 ネイティブ・クラスのエンコーディングはそれ以外何の文字列も必要としていません。特に、エンコードされたクラスのメンバやコンストラクタを持つことは要請されておりません。

[例:
public class N1 {
    char c[2];
    int i;
    double d;
public:
    void F() { … }
}; 
.class public sequential ansi sealed N1 extends
    [mscorlib]System.ValueType {
  .size 16
  .custom instance void [mscorlib]System.Runtime.CompilerServices.
    NativeCppClassAttribute::.ctor() = ( … )}
 この 16 バイトは char が1バイト、int は4バイト、double は8バイトを占め、char は任意のバウンダリで並べられ、int は4バイト・バウンダリで並べられ、double は8バイト・バウンダリで並べられるという実装に基づきます。(それは、二つの1バイト、2バイトのパッディング、一つの4バイト int、そして、一つの8バイト double となります。)
namespace MyApp { 
public class N2 { 
    char c[3];
    double d;
    int i;
public:
    void F(int i) { }
    class N3 {
        short int s;
    public:
        void F(int i) { }
    };
};
}
.class public sequential ansi sealed MyApp.N2 extends
     [mscorlib]System.ValueType {
  .size 24
  .custom instance void [mscorlib]System.Runtime.CompilerServices.
    NativeCppClassAttribute::.ctor() = ( … )
  .class sequential ansi sealed nested public N3 extends
     [mscorlib]System.ValueType {
    .size 2
    .custom instance void [mscorlib]System.Runtime.CompilerServices.
    NativeCppClassAttribute::.ctor() = ( … )
  }
}
 24 バイト長は、三つの1バイトchar、5バイトのパッディング、一つの8バイト double、一つの4バイト int、一つの2バイト short、2バイトのパッディングから来ています。2 バイト長は一つの2バイト short からです。
template<typename T>
public class N4 {
    T m1;
    T m2[2];
public: 
    void F(T t, T* pt) {}
};
N4<char> n4a;
N4<int> n4b;
.class public sequential ansi sealed 'N4<char>' extends
     [mscorlib]System.ValueType {
  .size 3
  .custom instance void [mscorlib]System.Runtime.CompilerServices.
    NativeCppClassAttribute::.ctor() = ( … )
}
.class public sequential ansi sealed 'N4<int>' extends
     [mscorlib]System.ValueType {
  .size 12
  .custom instance void [mscorlib]System.Runtime.CompilerServices.
    NativeCppClassAttribute::.ctor() = ( … )
}
 n4a と n4b のエンコーディングは示されません。

 テンプレート・クラスのメタデータは §34.17 に記述されています。

34.9 Refクラス

[注記:IsBoxed 修飾子を提供する実装のために: パラメータ宣言か値型へのハンドルを含む返値を持っている ref クラス、値クラス、ないし、インターフェイス・クラスの任意のメンバ関数は、そのパラメータや/と返値を IsBoxed(§33.1.5.1 )修飾子でマークされているべきです。]
 値渡しによって渡された ref クラス型パラメータを持つ ref クラス、値クラス、ないし、インターフェイス・クラスの任意のメンバ関数は修飾子 IsByValue(§33.1.5.2 )で対応するパラメータをマークされているべきです。
 const 修飾パラメータや返値が const 修飾されている ref クラス、値クラス、ないし、インターフェイス・クラスの任意のメンバ関数は、適切に、IsConst 修飾子(§33.1.5.3 )でマークされた、対応するパラメータと/や返値を持つべきです。しかしながら、上層でのパラメータ修飾はそのようにマークされているべきではありません。[例:const int* ci のようなパラメータはマークされていますが、const int i のようなものはマークされません。]
 const 修飾されている型の ref クラス、値クラス、ないし、インターフェイス・クラスの任意のデータ・メンバは IsConst(§33.1.5.3 )修飾子でマークされているべきです。
 interior ポインタやピンニング・ポインタであるパラメータを持つ ref クラス、値クラス、ないし、インターフェイス・クラスの任意のメンバ関数は IsExplicitlyDereferenced(§33.1.5.4 )修飾子でマークされた対応するパラメータを持つべきです。
 パラメータとして参照や追跡参照を持ったり、返値として参照や追跡参照を返す ref クラス、値クラス、ないし、インターフェイス・クラスの任意のメンバ関数は対応するパラメータと/や返値に IsImplicitlyDereferenced(§33.1.5.5 )修飾子を持つべきです。
 参照や追跡参照である ref クラス、値クラス、ないし、インターフェイス・クラスの任意のデータ・メンバは isImplicitlyDereferenced(§33.1.5.5 )修飾子でマークされているべきです。

[注記:IsLong 修飾子を提供する実装のために: パラメータ宣言や返値に long int や long double を含んでいる ref クラス、値クラス、ないし、インターフェイス・クラスの任意のメンバ関数はそのパラメータと/や返値を IsLong(§33.1.5.6 )修飾子でマークされているべきです。
long int や long double を含んでいる ref クラス、値クラス、ないし、インターフェイス・クラスのデータ・メンバはそのパラメータと/や返値を IsLong(§33.1.5.6 )修飾子でマークされているべきです。]
 素の char を含むパラメータ宣言や返値を持つ ref クラス、値クラス、ないし、インターフェイス・クラスの任意のメンバ関数はそのパラメータと/や返値は IsSignUnspecifiedByte(§33.1.5.7 )修飾子でマークされているべきです。
 素の char を含む ref クラス、値クラス、ないし、インターフェイス・クラスの任意のデータ・メンバは IsSignUnspecifiedByte(§33.1.5.7 )修飾子でマークされているべきです。
 値として ref クラスのインスタンスを返す ref クラス、値クラス、ないし、インターフェイス・クラスの任意のメンバ関数は isUdtReturn(§33.1.5.8 )修飾子でマークされているべきです。
 volatile 修飾されたパラメータや volatile 修飾された値を返す ref クラス、値クラス、ないし、インターフェイス・クラスの任意のメンバ関数は対応するパラメータと/や返値は、適切に、IsVolatile(§33.1.5.9 )修飾子でマークされているべきです。しかしながら、上層のパラメータ修飾子はマークされるべきではありません。[例:volatile int* vi のようなパラメータはマークされるべきですが、volatile int v のようなものはマークされるべきではありません。]
 volatile 修飾された型を持つ ref クラス、値クラス、ないし、インターフェイス・クラスの任意のデータ・メンバは IsVolatile(§33.1.5.9 )でマークされるべきです。
 詳細な情報は §34.7.1 を参照のこと。

34.10 値クラス

 詳細な情報は§34.7.1 §34.9 を参照のこと。

34.11 CLI 配列

 CLI 配列は CLI 標準、基本的にパーティション I、II、III、によってメタデータ中にエンコード化されます。[注意:CLI 配列型は CLI 配列の要素型、CLI 配列のランク、そして、CLI 配列のそれぞれの次元の上限下限を指定することで定義されるべきです。

 CLI 配列要素は行大順に CLI 配列オブジェクトの中に配置されているべきです。各々の CLI 配列要素が領域確保されている実際のストレージはプラットフォーム指定の割付を含めることができます。
 VES は配列に二つのコンストラクタを提供します。
  • 最初のコンストラクタは各々の次元ごとに要素の数を与える整数の順番を取ります。(下限は0が仮定されます。)
  • 二番目のコンストラクタは多くの引数として二倍を取ります。これらの引数は--各次元ごとに一組の--対として、それぞれの対の最初の引数は次元の下限を、二番目の引数はその次元の全要素数を指定して、発生します。
 配列コンストラクタに加えて、VES は Get、Set と特定の要素にアクセスしそのアドレスを計算する Addressインスタンス・メソッドを提供します。これらのメソッドは目標となる要素を特定するためにそれぞれの次元の数を取ります。加えて、Set は目標の要素に蓄えられる値を指定するための最後の引数を追加で取ります。]
[例:
ref class R {
    array<int>^ m1;
    array<array<String^>^, 2>^ m2;
public:
    array<String^, 2>^ F(array<R^, 3>^ ary) { … }
};
.class … R … {
  .field private int32[] m1
  .field private string[][0...,0...] m2
  .method public instance string[0...,0...]
    F(class R[0...,0...,0...] ary) … { … }
}
array<int>^ array1D = gcnew array<int>(10);
array<int, 3>^ array3D = gcnew array<int, 3>(10, 20, 30);
pin_ptr<int> pp1;
.method … {
  .locals ([0] int32[0...,0...,0...] V_0,
           [1] int32[] V_1)
           [2] int32& pinned modopt([mscorlib]
            System.Runtime.CompilerServices.IsExplicitlyDereferenced)
            V_2)
  ldnull
  stloc.1
  ldnull
  stloc.0

  ldc.i4.s 10
  newarr [mscorlib]System.Int32
  stloc.1
  ldloc.1
  ldc.i4.5
  ldc.i4.s   10
  stelem.i4
  ldc.i4.s   10
  ldc.i4.s   20
  ldc.i4.s   30
  newobj     instance void int32[0...,0...,0...]::.ctor(int32,
                  int32, int32)
  stloc.0
array1D[5] = 10;
array3D[1,2,3] = array3D[4,5,6];
  ldloc.0
  ldc.i4.1
  ldc.i4.2
  ldc.i4.3

  ldloc.0
  ldc.i4.4
  ldc.i4.5
  ldc.i4.6
  call     instance int32 int32[0...,0...,0...]::Get(int32,
                int32, int32)
  call     instance void int32[0...,0...,0...]::Set(int32,
                int32, int32, int32)
pp1 = &array1D[8];
pp1 = &array3D[7,6,5];
  stloc.0
  ldloc.1
  ldc.i4.8
  ldelema  [mscorlib]System.Int32
  stloc.2
  ldloc.0
  ldc.i4.7
  ldc.i4.6
  ldc.i4.5
  call     instance int32& int32[0...,0...,0...]::Address(int32,
                int32, int32)


34.12 インターフェイス

 インターフェイス・クラスは対応した名前と視認性のクラスとして発行されるべきです。それは interface とマークされます。インターフェイス・クラスはクラスなので、§34.7 §34.9 クラスとそのメンバに属するメタデータの詳細はその従節を参照してください。
 全てのインターフェイス・クラス・メンバ関数は newslot, abstract、そして、virtual でマークされた .method として発行されるべきでしょう。[例:
public interface struct I {
    void F();
    property int P {
        int get();
        void set(int value);
    }
};
.class interface public abstract … I {
  .method public newslot abstract virtual instance void F() … { … }

  .property instance int32 P() {
    .get instance int32 I::get_P()
    .set instance void I::set_P(int32)
}
.method public newslot … abstract virtual … int32 get_P() … { … }
.method public newslot … abstract virtual … void set_P(int32 value)
    … { … }
}

[例:
public interface struct I1 {
    void F();
};
public interface struct I2 : I1 {
    void G();
    void K();
};
public ref struct B {
    virtual void K() { … }
};
public ref struct D : B, I2 {
    virtual void F() { … }            // I1::F を実装
    virtual void H() = I2::G { … }    // I2::G を実装
    virtual void G() new { … }        // 新しい G
};                                     // I2::K は B::K で実装される
public ref struct E abstract : I1 {
    virtual void F() abstract;
};
.class interface public abstract … I1 {
  .method public newslot abstract virtual instance void F() … { … }
}
.class interface public abstract … I2 implements I1 {
  .method public newslot abstract virtual instance void G() … { … }
  .method public newslot abstract virtual instance void K() … { … }
}
.class public … B … {
  .method public newslot virtual instance void K() … { … }
}
.class public … D extends B implements I2 {
  .method public virtual instance void F() … { … }
  .method public newslot virtual final instance void H() … {
    .override I2::G
    …
  }
  .method public newslot virtual instance void G() … { … }
}
.class public abstract … E … implements I1 {
  .method public abstract virtual instance void F() … { … }
}


34.13 列挙型

 ネイティブ、CLI 双方の配列型で sealed クラスとして System::Enum から継承されたクラスが実装されているべきです。 列挙型の視認性はそのクラス定義に反映されています。それぞれの列挙型クラスは value__ と呼ばれる、型がその列挙型の背景型となり、CLS-コンプライアント整数型であるべき、インスタンス・フィールドを含むべきでしょう。そのフィールドは rtspecialname と specialname でマークされているべきです。(フィールドに指定する情報は §34.7.3 を参照のこと)
 CLI 列挙型中のそれぞれの列挙子はその同じ名前の対応した public static literal なフィールドを持っているべきで、その型は親となる列挙型のものであり、その値は enum-specifier(列挙指定子)で定義されたものです。[注意:ネイティブ列挙型の列挙子はそのような対応したフィールドを持ちません。結果として、分割されたコンパイルでそれらの値を共有するには、ヘッダが使用されるべきでしょう。]
[例:
public enum Suit : short { Hearts = 1, Spades, Clubs, Diamonds};

enum class Direction { North, South = 10, East, West = 20 }; 
.class public … sealed Suit extends [mscorlib]System.Enum {
  .field public specialname rtspecialname int16 value__
}
.class private … sealed Direction extends [mscorlib]System.Enum {
  .field public static literal valuetype Direction East = int32(0x0B)
  .field public static literal valuetype Direction North = int32(0x00)
  .field public static literal valuetype Direction South = int32(0x0A)
  .field public static literal valuetype Direction West = int32(0x14)
  .field public specialname rtspecialname int32 value__
}


34.14 デリゲート

 デリゲートは(最終的には)System::Delegate から継承された sealed クラスとして実装されるべきです。 [注意:しかしながら、デリゲート・クラスはこのクラスから直接的に派生する必要はありません。CLI に適合した実装は中間的な型に含まれた必要な型階層を拡張することが許されています。例えば、CLI に適合した実装は System::MulticastDelegate 、翻って、System::Delegate から派生した、型を提供することができます。そのような適合した C++/CLI 実装はそのデリゲート・クラスを System::MulticastDelegate やそのクラスから派生したクラスから派生することができます。]
 デリゲート型の視認性はそのクラス定義に反映されるべきです。
 各々のデリゲート型クラスに、適合した実装は、CLI標準で定義されたコンストラクタ、Invoke と呼ばれるメソッド、そして、 BeginInvoke と EndInvoke メソッド(非同期実行に使われる)を提供するべきです。
[例:
public delegate Object^ D(int* pi, array<int>^ a);
.class public … sealed D extends [mscorlib]System.Delegate { 
  .method public specialname rtspecialname instance void
      .ctor(object A_1, native int A_2) runtime managed forwardref {}
  .method public newslot virtual instance class
      [mscorlib]System.IAsyncResult BeginInvoke(int32* pi, int32[] a,
         class [mscorlib]System.AsyncCallback callback, object obj)
         runtime managed forwardref {}
  .method public newslot virtual instance object
      EndInvoke(class [mscorlib]System.IAsyncResult result)
         runtime managed forwardref {}
  .method public newslot virtual instance object Invoke(int32* pi,
      int32[] a) runtime managed forwardref {}
}

 §27.2 において、"それぞれのデリゲート型は次のような二つのコンストラクタを持つべきです・・・"と明記されています。ライブラリ・クラス System::Delegate は定義されているコンストラクタを持っていません。その代わり、私たちが上のメタデータの例で見ることができるように、一つ、そして、たった一つのコンストラクタがデリゲートに生成されており、その実装属性は cil managed の代わりに runtime managed です。これはコンストラクタは VES によって実行時に生成されるためです。C++/CLI の文法はそれぞれひとつとふたつの引数を持つデリゲート・コンストラクタ呼び出しをサポートしていますが、双方の形とも、メタデータ中に実際に存在する一つのコンストラクタの呼び出しに変換されているべきです。一つの引数を持つ C++/CLI コンストラクタは最初の引数として nullptr を二つの引数バージョンに渡されて呼び出しが発行されるべきです。
[例:
delegate void D(int i);

ref struct R {
    static void M1(int a) { }
    void M2(int b) { }
    virtual void M3(int c) { }
};
int main() {
    R^ r = gcnew R;
    D^ d;
    d = gcnew D(&R::M1);
    d = gcnew D(r, &R::M2);
    d += gcnew D(r, &R::M3);
}
.method … main() … {
  …
  .locals ([0] class D V_0,
           [1] class R V_1)
  ldnull 
  stloc.1 
  ldnull 
  stloc.0
  newobj      instance void R::.ctor()
  stloc.1

  ldnull
  ldftn       void R::M1(int32)
  newobj      instance void D::.ctor(object, native int)
  stloc.0

  ldloc.1
  ldftn       instance void R::M2(int32)
  newobj      instance void D::.ctor(object, native int)
  stloc.0

  ldloc.0
  ldloc.1
  dup
  ldvirtftn   instance void R::M3(int32)
  newobj      instance void D::.ctor(object, native int)
  call        class [mscorlib]System.Delegate
                [mscorlib]System.Delegate::Combine(
                class [mscorlib]System.Delegate,
                class [mscorlib]System.Delegate)
  castclass   D
  stloc.0
  …
}


34.15 例外

 try, catch、そして、finally は一つ以上の .try 指定子を使うことで発行されるべきです。[例:
int main() {
    try {
        // ...
    }
    catch (NullReferenceException^ ex1) {
        // ...
    }
    catch (IndexOutOfRangeException^ ex2) {
        // ...
    }
    finally {
        // ... 
    }
}
.method … main() …
{
  …
  .locals ([0] class [mscorlib]System.IndexOutOfRangeException ex2,
           [1] class [mscorlib]System.NullReferenceException ex1)
  .try
  {
    .try
    {
      …
      leave.s  L8
    }
    catch [mscorlib]System.NullReferenceException
    {
      …
      stloc.1
      leave.s  Le
    }
    catch [mscorlib]System.IndexOutOfRangeException
    {
      …
      stloc.0
      leave.s  La
    }
L8:   br.s     Lc
La:   leave.s  L13
Lc:   br.s     L10
Ie:   leave.s  L13
I10:  leave.s  L13
  }
  finally
  {
      …
      endfinally
  }
L13: …
     …
}

 非 ref 型クラス型宣言やその形態...を持っているような exception-declaration(例外宣言)のためのメタデータ・エンコーディングは未規定です。

34.16 属性

 もし、コンパイラによって消滅されることが要請されなければ、プログラム要素上の属性はその要素上に、ないし、いくつかの場合では、要素宣言に先行して直ちに .custom 指定子を通してメタデータ中に発行されるべきです。もし、プログラム要素が複数の属性を持ち、複数属性が許されれば、その要素はそれぞれごとに一つの .custom 指定子を持つべきです。それらの順序は無関係です。
 カスタム属性は .custom 指定子を使って、型コンストラクタ(例えば、そのメソッドの名前は .ctor であるべき)のためのメソッド宣言に付き従われ、オプションとして括弧中のバイト値のセットと等価記号(=)によって伴われ、宣言されます。コンストラクタの引数の値は、もし少しであっても、CLI 標準で規定された書式でバイトのセットが指定されるべきでしょう。もし、引数が存在しないのであれば、等価記号と括弧でくくられたバイト値のセットは省略されるべきです。コンストラクタはインスタンス・メソッドなので、その .custom 指定子は instance 属性を含むべきです。[例:
[AttributeUsage(AttributeTargets::All, AllowMultiple = true,
    Inherited = true)]
public ref class XAttribute : Attribute {
    String^ name;
public:
    XAttribute(String^ name) : name(name) {}
    property String^ Name { String^ get() { return name;} }
};
.class public … XAttribute extends [mscorlib]System.Attribute {
  .custom instance void
    [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype
    [mscorlib]System.AttributeTargets) = ( 01 00 FF 7F 00 00 02 00 54
    02 0D 41 6C 6C 6F 77 4D 75 6C 74 69 70 6C 65 01 54 02 09 49 6E 68
    65 72 69 74 65 64 01)
  …
}
[X("refclass")]
 public ref class R {
     [X("field")] int count;
 public: 
     [X("constructor")] R() {}
 };
.class … R … {
  .custom instance void XAttribute::.ctor(string) = ( 01 00 08 72 65
     66 63 6C 61 73 73 00 00 ) // refclass

  .field private int32 count
  .custom instance void XAttribute::.ctor(string) = ( 01 00 05 66 69 65
6C 64
     00 00 ) // field
  .method public specialname rtspecialname instance void .ctor() cil … {
    .custom instance void XAttribute::.ctor(string) = ( 01 00 0B 63 6F
      6E 73 74 72 75 63 74 6F 72 00 00 ) // constructor
  }
}
[X("valueclass")]
public value struct V {
    [X("method1"),X("method2")] [returnvalue:X("returnvalue")]
    void Display([X("parameter")] int i) {}
};
.class … V … {
  .custom instance void XAttribute::.ctor(string) = ( 01 00 0A 76 61
      6C 75 65 63 6C 61 73 73 00 00 ) // valueclass
  .method … void Display(int32 i) … {
    .custom instance void XAttribute::.ctor(string) = ( 01 00 07 6D 65
        74 68 6F 64 3200 00 ) //method2
    .custom instance void XAttribute::.ctor(string) = ( 01 00 07 6D 65
        74 68 6F 64 3100 00 ) //method1
    .param [0]
    .custom instance void XAttribute::.ctor(string) = ( 01 00 0B 72 65
        74 75 72 6E 76 61 6C 75 65 00 00 ) // returnvalue
    .param [1]
    .custom instance void XAttribute::.ctor(string) = ( 01 00 09 70 61
        72 61 6D 65 74 65 72 00 00 ) // parameter
  }
}
 .param[0] は関数の返値を表し、実際のパラメータ属性は .para,[1] から始まります。
[X("interfaceclass")]
public interface class I {
    [X("property")]property int Count {
        [X("getter")]int get();
    }
};
.class interface … I {
  .custom instance void XAttribute::.ctor(string) = ( 01 00 0E 69 6E
     74 65 72 66 61 63 65 63 6C 61 73 73 00 00 ) // interfaceclass
  .property instance int32 Count() {
    .custom instance void XAttribute::.ctor(string) = ( 01 00 08 70 72
       6F 70 65 72 74 79 00 00 ) // property
    .get instance int32 I::get_Count()
  }
  .method public … get_Count() … {
    .custom instance void XAttribute::.ctor(string) = ( 01 00 06 67 65
      74 74 6572 00 00 ) // getter
  }
}
[X("nativeclass")]
public class N {
    [X("field")] int count;
public:
    [X("constructor")] N() { … }
    [X("method")][returnvalue:X("returnvalue")]
    void Display([X("parameter")] int) {}
};
.class … N … {
  .custom instance void XAttribute::.ctor(string) = ( 01 00 0B 6E 61 74
69 76
    65 63 6C 61 73 73 00 00 ) // nativeclass
}
 ネイティブ・クラスのメンバ情報はメタデータに発行する必要はありません。ただ、.custom 指定子がそのクラス自身に必要とされています。]
 属性はメタデータをカスタマイズするために使うことができるため、それらはしばしば custom attribute と呼ばれます。カスタム属性には二種類:純粋カスタム属性と擬似カスタム属性、があります。カスタム属性と擬似カスタム属性は、以下のように、それらが定義された時に、異なった取り扱いを受けます。
  • カスタム属性は直接、メタデータ中に埋め込まれます。その定義データである一滴はそのまま格納されます。その一滴は後ほど取得することができます。
  • 擬似カスタム属性は、その名前がある短いリストの要素の一つであるので、認識されます。その一滴はメタデータ中に直接的に格納されるよりも、その一滴はパースされ、それが含んでいる情報はメタデータ・テーブルの中で、ビットの and/or フィールドのセットとして使われます。その一滴が破棄されると、それを後から取り出すことはできません。
 擬似カスタム属性は、それ故に、コンパイラが純粋カスタム属性に提供する同じような構文を使って、ユーザー指定子を捉えるのに役立ちますが、これらのユーザー指定子はその時、メタデータ・テーブルからより領域として効果的に保持されます。テーブルはまた純粋カスタム属性よりも実行時に拘束に検証されます。
 多くのカスタム属性はソフトウェアの高いレイヤーにおいて作り上げられています。これらはCLIによって、それが何を意味するのか知ることなく、注意することなく、格納され、返却されます。しかしながら、全ての擬似カスタム属性が、加えてある純粋カスタム属性のコレクションが、コンパイラと CLI に特別な関心の元になっています。CLI 標準、パーティションII、21章は擬似カスタム属性とカスタム属性の区別をリストしています。区別は CLI と/やコンパイラがそれらに直接的な注意を払うことを意味しており、その振る舞いが同様に影響を与えることを意味しています。
 特殊な処理が様々な擬似カスタム属性に必要とされており、この節のそこらかしこらに記述されています。例としては DllImportAttribute、FieldOffsetAttribute、InAttribute、MarshalAsAttribute、MethodImplAttribute、OutAttribute、そして、StructLayoutAttribute が含まれます。
 準拠した実装は( System 名前空間の)AttributeUsageAttribute 属性に注意を払う必要があります。
 パラメータ配列省略記法(...)は( System 名前空間中の) ParamArrayAttribute 属性のための .custom 指定子の生成を含みます。§34.6.1 を参照のこと。

34.17 テンプレート

 テンプレート・クラスとテンプレート関数のメタデータ・エンコーディングは、任意の発行されたテンプレートクラスの名前は CLI-compliant な振る舞いで記述されているべきでないことを除いて、未規定です。

34.18 ジェネリクス

 ジェネリック型の名前は C++/CLI ソース中で指定されたその型名、加えて、n がその型のアリティを示す(0以外の)十進数整数定数である 'n 形式の後置子 であるべきです。非ジェネリック型のメタデータ中の名前はそのような後置子を持つべきではありません。[例:
ref class X { ... };
// メタデータ型名は X
.class public ... X ... { ... }
generic<typename T>
public ref class X { ... };
// メタデータ型名は X`1
.class public ... X`1< ... T> ... { ... }
generic<typename T, typename U>
public ref class X {
public:
    ref class Y { ... };
    generic<typename A>
    ref class Z { ... };
};
// メタデータ型名はX`2
.class public ... X`2< ... T, ... U> ... {
    // メタデータ名は Y
    .class ... nested public Y<( ... T, ... U> ... { ... }
    // メタデータ名は Z`1
    .class ... nested public Z`1<( ... T, ... U, ... A> ... { ... }
}


文責:翻訳 => ヽ(゚∀。)ノうぇね