TECH MEDIA

テックメディア


その他
ブログ

Attribute使用した保守性の上がるコードの書き方

その他技術
目次
  1. 01|はじめに
  2. 02|Attribute(アトリビュート)とは?
  3. 03|Attributeの使い方
  4. 04|アトリビュートを実装してみる
  5. 05|最後に

1. はじめに

始めまして、エンジニアの朴・小林です。
ここでは保守性が高まる.NETのAttribute(アトリビュート)について説明していきます。

2. Attribute(アトリビュート)とは?

属性とも呼ばれる、クラス・構造体、または、そのメンバー(メゾット・プロパティなど)に対して付与できる追加情報です。属性をプログラム要素に関連付けると、リフレクションと呼ばれる手法を使用して、実行時にその属性を照会することができます。

例えば:PersonというクラスにHand、Foot、Face等のプロパティが存在し、Walk(),Eat()等のメソッドが存在する。

Personというクラスに[police]というAttributeを付けると、このPersonが警察だとわかる、同様に[engineer]を付けると、このPersonがエンジニアだと分かるというイメージを持っていただければと思います。

3. Attributeの使い方

■Attributeはクラス                                 Attributeを使用する場合、必ずSystem.Attribute.を継承する必要がある

■Attributeの名前                           一般的に、〇〇Attributeとする。                     利用する際にAttributeを省略することができる。
例:TestAttributeは利用時に[Test]と省略ができる

4. アトリビュートを実装してみる

例えば、あるクラスのUserが下記の状態だとする。
Normalの場合は「通常ユーザー」を出力
Frozenの場合は「利用中止ユーザー」を出力
Deletedの場合は「退会ユーザー」を出力

<span class="token keyword">public</span> <span class="token keyword">enum</span> UserState
<span class="token punctuation">{</span>
    Nomal <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token comment">//通常</span>
    Frozen <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token comment">//利用できない</span>
    Deleted <span class="token operator">=</span> <span class="token number">2</span> <span class="token comment">//退会済</span>
<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span>

一般的に、下記の様なswitch文のロジックを組むと思いますが、
これだと、状態を追加するほど、分岐の条件も増やさないといけないので、保守性も非常い低いです。
ここでAttributeを利用すればこれらの事が解決できます。

<span class="token keyword">switch</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">case</span> UserState<span class="token punctuation">.</span>Nomal<span class="token punctuation">:</span>
        Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span><span class="token string">"通常ユーザー"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">break</span><span class="token punctuation">;</span>

    <span class="token keyword">case</span> User<span class="token punctuation">.</span>Frozen<span class="token punctuation">:</span>
        Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span><span class="token string">"利用中止ユーザー"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">break</span><span class="token punctuation">;</span>

     <span class="token keyword">case</span> User<span class="token punctuation">.</span>Deleted<span class="token punctuation">:</span>
        Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span><span class="token string">"退会ユーザー"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">break</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span>

↓Attributeを使用すると

<span class="token comment">//アトリビュートクラスを作成</span>
<span class="token keyword">public</span> <span class="token keyword def">class</span> <span class="token class-name">RemarkAttribute</span> <span class="token punctuation">:</span> <span class="token class-name">Attribute</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token function">RemarkAttribute</span><span class="token punctuation">(</span><span class="token keyword">string</span> remark<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword this">this</span><span class="token punctuation">.</span>_Remark <span class="token operator">=</span> remark<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">string</span> _Remark <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">string</span> <span class="token function">GetRemark</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword this">this</span><span class="token punctuation">.</span>_Remark<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">//Remarkのアトリビュートを追加</span>
<span class="token keyword">public</span> <span class="token keyword">enum</span> UserState
<span class="token punctuation">{</span>
    <span class="token punctuation">[</span><span class="token class-name">Remark</span><span class="token punctuation">(</span><span class="token string">"通常ユーザー"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    Nomal <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token comment">//通常</span>
    <span class="token punctuation">[</span><span class="token class-name">Remark</span><span class="token punctuation">(</span><span class="token string">"利用中止ユーザー"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    Frozen <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token comment">//利用できない</span>
    <span class="token punctuation">[</span><span class="token class-name">Remark</span><span class="token punctuation">(</span><span class="token string">"退会ユーザー"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    Deleted <span class="token operator">=</span> <span class="token number">2</span> <span class="token comment">//退会済</span>
<span class="token punctuation">}</span>

<span class="token comment">//アトリビュートの機能を作成</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword def">class</span> <span class="token class-name">RemarkExtension</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">string</span> <span class="token function">GetRemark</span><span class="token punctuation">(</span><span class="token keyword this">this</span> <span class="token class-name">Enum</span> <span class="token keyword">value</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token comment">//反射、リフレクションを利用。ネットで調べてください</span>
     <span class="token comment">//valueのタイプを取得</span>
        <span class="token class-name">Type</span> type <span class="token operator">=</span> <span class="token keyword">value</span><span class="token punctuation">.</span><span class="token function">GetType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">//フィールドを取得</span>
        <span class="token class-name">FieldInfo</span> field <span class="token operator">=</span> type<span class="token punctuation">.</span><span class="token function">GetField</span><span class="token punctuation">(</span><span class="token keyword">value</span><span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
     <span class="token comment">//フィールドにRemarkAttributeが定義されていれば</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>field<span class="token punctuation">.</span><span class="token function">IsDefined</span><span class="token punctuation">(</span><span class="token keyword">typeof</span><span class="token punctuation">(</span>RemarkAttribute<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">true</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token class-name">RemarkAttribute</span> attribute <span class="token operator">=</span>
                <span class="token punctuation">(</span>RemarkAttribute<span class="token punctuation">)</span>field<span class="token punctuation">.</span><span class="token function">GetCustomAttribute</span><span class="token punctuation">(</span><span class="token keyword">typeof</span><span class="token punctuation">(</span>RemarkAttribute<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">return</span> attribute<span class="token punctuation">.</span><span class="token function">GetRemark</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">else</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">return</span> <span class="token keyword">value</span><span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span>

上記を実現すると、下記のようなコードがいらなくなる。

<span class="token keyword">switch</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">case</span> UserState<span class="token punctuation">.</span>Nomal<span class="token punctuation">:</span>
        Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span><span class="token string">"通常ユーザー"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">break</span><span class="token punctuation">;</span>

    <span class="token keyword">case</span> User<span class="token punctuation">.</span>Frozen<span class="token punctuation">:</span>
        Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span><span class="token string">"利用中止ユーザー"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">break</span><span class="token punctuation">;</span>

     <span class="token keyword">case</span> User<span class="token punctuation">.</span>Deleted<span class="token punctuation">:</span>
        Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span><span class="token string">"退会ユーザー"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">break</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span>

<span class="token comment">//stateがRemarkアトリビュートがある場合、アトリビュートのRemarkをそのまま取得できる、ない場合は、stateそのままになる。</span>
Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>state<span class="token punctuation">.</span><span class="token function">GetRemark</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span>

また、UserクラスにAgeというプロパティが存在していて、データベースへ挿入する際に、年齢が18から30までのユーザーしか挿入させないように制限したい。

まず、AgeValidationAttributeというクラスを宣言

<span class="token keyword">public</span> <span class="token keyword def">class</span> <span class="token class-name">AgeValidationAttribute</span> <span class="token punctuation">:</span> <span class="token class-name">Attribute</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token function">AgeValidationAttribute</span><span class="token punctuation">(</span><span class="token keyword">int</span> min<span class="token punctuation">,</span> <span class="token keyword">int</span> max<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword this">this</span><span class="token punctuation">.</span>_Min <span class="token operator">=</span> min<span class="token punctuation">;</span>
        <span class="token keyword this">this</span><span class="token punctuation">.</span>_Max <span class="token operator">=</span> _Max<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">int</span> _Min <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">int</span> _Max <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>

    <span class="token comment">//バリデーション</span>
    <span class="token keyword">public</span> <span class="token keyword">bool</span> <span class="token function">Validate</span><span class="token punctuation">(</span><span class="token keyword">object</span> <span class="token keyword">value</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">value</span> <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&&</span> <span class="token operator">!</span><span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrWhiteSpace</span><span class="token punctuation">(</span><span class="token keyword">value</span><span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">.</span><span class="token function">TryParse</span><span class="token punctuation">(</span><span class="token keyword">value</span><span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">out</span> <span class="token keyword">int</span> resut<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>resut <span class="token operator">></span> <span class="token keyword this">this</span><span class="token punctuation">.</span>_Min <span class="token operator">&&</span> resut <span class="token operator"><</span> <span class="token keyword this">this</span><span class="token punctuation">.</span>_Max<span class="token punctuation">)</span>
                <span class="token punctuation">{</span>
                    <span class="token keyword">return</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">return</span> <span class="token keyword">false</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span>

次はアトリビュートの機能を実現するためのStaticメソッドを宣言

<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword def">class</span> <span class="token class-name">ValidateExtension</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">bool</span> <span class="token function">Validate</span><span class="token punctuation">(</span><span class="token keyword this">this</span> <span class="token keyword">object</span> <span class="token keyword">value</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token class-name">Type</span> type <span class="token operator">=</span> <span class="token keyword">value</span><span class="token punctuation">.</span><span class="token function">GetType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> prop <span class="token keyword">in</span> type<span class="token punctuation">.</span><span class="token function">GetProperties</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>prop<span class="token punctuation">.</span><span class="token function">IsDefined</span><span class="token punctuation">(</span><span class="token keyword">typeof</span><span class="token punctuation">(</span>AgeValidationAttribute<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">true</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                <span class="token class-name">AgeValidationAttribute</span> attribute <span class="token operator">=</span>
                    <span class="token punctuation">(</span>AgeValidationAttribute<span class="token punctuation">)</span>prop<span class="token punctuation">.</span><span class="token function">GetCustomAttribute</span><span class="token punctuation">(</span><span class="token keyword">typeof</span><span class="token punctuation">(</span>RemarkAttribute<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>attribute<span class="token punctuation">.</span><span class="token function">Validate</span><span class="token punctuation">(</span>prop<span class="token punctuation">.</span><span class="token function">GetValue</span><span class="token punctuation">(</span><span class="token keyword">value</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token keyword">false</span><span class="token punctuation">)</span>
                <span class="token punctuation">{</span>
                    <span class="token keyword">return</span> <span class="token keyword">false</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">return</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span>

最後に、プロパティAgeにAgeValidationアトリビュートを追加

<span class="token punctuation">[</span><span class="token class-name">AgeValidation</span><span class="token punctuation">(</span><span class="token number">18</span><span class="token punctuation">,</span><span class="token number">30</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">int</span> Age <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span>

利用時

user<span class="token punctuation">.</span><span class="token function">Validate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

5. 最後に

アトリビュートを使用すると保守性の高いコードを実現することができます。 是非利用してみてください。

RECRUIT 採用情報

「eビジネスに関わる全ての人を幸せにする」
私達とともに新たな時代をつくりませんか?