ユーザー定義属性(UDA)
あらゆる宣言(構造体型、クラス型、変数など)に属性を割り当てることができ、その属性はコンパイル時にアクセスしてコードのコンパイル方法を変更することができる。ユーザー定義属性は、純粋にコンパイル時の機能だ。
ユーザー定義属性の構文は、@記号とそれに続く属性で構成され、属性が割り当てられる宣言の前に記述する。例えば、次のコードは、Encrypted属性をnameの宣言に割り当てている。
複数の属性は、個別に指定することも、括弧で囲んだ属性のリストとして指定することもできる。例えば、次の2つの変数は同じ属性を持っている。
属性は、型名だけでなく、ユーザー定義型または基本型の値にもすることができる。ただし、その意味が明確でない場合があるため、42のようなリテラル値で構成される属性は使用しないことをお勧めする。
上記のaとbの属性は、種類が異なる。aの属性は型Encrypted自体だが、bの属性は型 Encryptedのオブジェクトだ。これは、コンパイル時に属性の使用方法に影響を与える重要な違いだ。この違いの例を以下に示す。
属性の意味は、プログラムの要件に応じてプログラマーが独自に決定する。属性は、コンパイル時に__traits(getAttributes)によって決定され、その属性に従ってコードがコンパイルされる。
以下のコードは、特定のstructメンバー(例:Person.name)の属性を__traits(getAttributes)からアクセスする方法を示している:
プログラムの出力には、Person.nameの属性が一覧表示される:
Encrypted
Colored(cast(Color)1)
ユーザー定義の属性を扱う場合、次の2つの__traits式も便利だ。
-
__traits(allMembers)は、型(またはモジュール)のメンバーを文字列として生成する。 -
__traits(getMember)は、メンバーにアクセスするときに便利なシンボルを生成する。最初の引数はシンボル(型名や変数名など)で、2番目の引数は文字列だ。最初の引数、ドット、2番目の引数を組み合わせてシンボルを生成する。例えば、__traits(getMember, Person,"name")Person.nameというシンボルを生成する。
プログラムの出力は、Personのすべてのメンバーのすべての属性を一覧表示する:
| nameの属性 | Encrypted Colored(cast(Color)1) |
|---|---|
| lastNameの属性 | |
| addressの属性 | Colored(cast(Color)2) |
もう1つの便利なツールはstd.traits.hasUDAで、シンボルが特定の属性を持つかどうかを判定する。次のstatic assertは、Person.nameがEncrypted属性を持つため、パスする:
hasUDAは、属性型だけでなく、その型の特定の値とも使用できる。次のstatic assertは、Person.nameにColored(Color.blue)属性があるため、両方ともチェックに合格する。
例
structオブジェクトのすべてのメンバーの値をXML形式で出力する関数テンプレートを設計しよう。次の関数は、出力時に各メンバーのEncryptedおよびColored属性を考慮する。
コードのハイライト部分は以下で説明する:
- 型のメンバーは、
__traits(allMembers)によって決定される。 - 各メンバーの値は、後で出力に表示する際に使用するために、
stringに変換される。例えば、メンバーが"name"の場合、右側の式はobject.name.to!stringになる。 - 各メンバーは、
hasUDAでテストされ、Encrypted属性があるかどうかが判断される。その属性がある場合、メンバーの値は暗号化される。(hasUDAはシンボルで動作するため、__traits(getMember)を使用してメンバーをシンボルとして取得する方法(例:Person.name)に注意。) - 各メンバーの色属性は、
colorAttributeOf()で決定される。これは後で説明する。
colorAttributeOf()関数テンプレートは、次のコードのように実装できる。
コンパイル時の評価が完了すると、printAsXML()関数テンプレートは、Person型に対して、以下の関数と同等のインスタンス化される。
完全なプログラムにはさらに説明がある:
プログラムの出力は、メンバーが正しい色を持ち、nameメンバーが暗号化されていることを示している:
<Person>
<name color="blue">Bmjdf</name> ← 青色で暗号化済み
<lastName color="black">Davignon</lastName>
<address color="red">Avignon</address> ← 赤
</Person>
<Person>
<name color="blue">Cfo</name> ← 青色で暗号化済み
<lastName color="black">de Bordeaux</lastName>
<address color="red">Bordeaux</address> ← 赤
</Person>
ユーザー定義属性の利点
ユーザー定義属性の利点は、プログラムの他の部分を変更することなく、宣言の属性を変更できることだ。例えば、以下の簡単な変更により、PersonのすべてのメンバーをXML出力で暗号化することができる。
出力:
<Person>
<name color="black">Djoez</name> ← 暗号化済み
<lastName color="black">ef!Dbooft</lastName> ← 暗号化済み
<address color="black">Dbooft</address> ← 暗号化済み
</Person>
さらに、printAsXML()およびそれが考慮する属性は、他の型でも使用することができる。
出力:
<Data>
<message color="blue">hello world</message> ← 青
</Data>
要約
- ユーザー定義属性は、任意の宣言に割り当てることができる。
- ユーザー定義の属性は、型名だけでなく値にも指定できる。
- ユーザー定義属性は、
hasUDAおよび__traits(getAttributes)によってコンパイル時にアクセスされ、プログラムのコンパイル方法を変更することができる。