無差別に技術をついばむ鳥

情報処理技術全般を気まぐれにつつくゆるいブログです

C++/CLIをつつく12ープロパティ。データを守ろう。

こんピヨー(こんにちは)。 今回はC++/CLIでカプセル化を実現する 為の機能であるプロパティの実装をつついてみよう。 プロパティのありがたみを知るには、やっぱりプロパティ無い状態を見るのが一番。 さっそくプロパティが無い時を見てみよう。

#include "stdafx.h"
using namespace System;

namespace Sample
{

public ref class Bird
{
public:
	//フィールド
	String^ Name;
	Int32 Age;

	//メソッド
	void Talk( ) {
		Console::WriteLine( "私は{0}。年は{1}だよ。", 
                        this->Name, this->Age);
	}
};

}

using namespace Sample;
int main(array< System::String ^ > ^ args)
{
    Bird^ b = gcnew Bird() ;
    b->Name = "インドリ";
    b->Age = 29;
    b->Talk( );

    b->Name = "ドリィちゃん";
    b->Age = -20;
    b->Talk( );
    Console::WriteLine(
         "えぇ〜!、凄い年齢詐称だ・・・" );
    return 0;
}

ドリィちゃんそこまで年齢を誤魔化さなくても・・・
インドリィちゃん「女性の年齢を聞くな!」
こんな具合にフィールドを直接扱えるようにしてしまったら入力値の妥当性をチェックできない んだ。では入力値をチェックするために値の設定にメソッドを設定してみよう、

#include "stdafx.h"
using namespace System;

namespace Sample
{

public ref class Bird
{
    //フィールド
private:
    Int32 m_age;
public:
    String^ Name;

    //メソッド
    void Talk( ) {
	Console::WriteLine( "私は{0}。年は{1}だよ。", this->Name, m_age); 
    }

    void SetAge( Int32 age ) { 
            if ( age > 0 ) m_age = age; 
    }
};

}

using namespace Sample;
int main(array< System::String ^> ^ args)
{
    Bird^ b = gcnew Bird() ;
    b->Name = "インドリ";
    b->SetAge(29);
    b->Talk( );

    b->Name = "ドリィちゃん";
    b->SetAge(-20);
    b->Talk( );
    Console::WriteLine(
         "ドリィちゃん、ボクと同じ年齢なんだね♪" );
    return 0;
}

これで年齢にマイナス値を入力したら、その罰として前の人の年齢が表示されるようになるよ。
インドリィちゃん「この屈辱忘れるものかぁぁ」
ビック!ちょっと後がまずいことになるかも・・・それはさておき、フィールドを保護するために一々Set○○、Get○○って 名前のメソッドを作るのは面倒だし見栄えが悪いよね。だからC++/CLIにはもっといい方法が用意されているんだ。早速見てみよう。
#include "stdafx.h"
using namespace System;

namespace Sample
{

public ref class Bird
{
    //フィールド
private:
    Int32 m_age;
    String^ m_name;
public:
    property Int32 Age {
        Int32 get() { return this->m_age; } //<ーツンツン
	void set( Int32 value ) { //<ーツンツン
            if ( value > 0 ) m_age = value;
	}
     }

    property String^ Name {
        String^ get() { return this->m_name; } //<ーツンツン
	void set( String^ value ) { this->m_name = value; } //<ーツンツン
    }

    //メソッド
     void Talk( ) {
         Console::WriteLine( "私は{0}。年は{1}だよ。", 
             this->m_name, this->m_age); 
     }
};

}

using namespace Sample;
int main(array< System::String ^> ^ args)
{
	Bird^ b = gcnew Bird() ;
        b->Name = "インドリ";
	b->Age = 29;
        b->Talk( );

	b->Name = "ドリィちゃん";
	b->Age = -20;
        b->Talk( );
	Console::WriteLine(
             "ドリィちゃん、ボクと同じ年齢なんだね♪" );
        return 0;
}

この構文がプロパティなんだ。この構文を詳しく突付いてみよう。
まずは始めのプロパティの宣言は・・・
property Int32 { プログラム } 

プロパティが見える範囲(C++/CLIではまとめて定義)、 ってなっている。これはプロパティの型プロパティの名前の三つを指定しているんだ。
プロパティが見える範囲については色々あるから説明は後回しで、型と名前を考えてみよう。 型と名前はもうお馴染だよね?考え方はフィールド(変数)と変わらないピヨ。
propertyキーワード以外のプロパティとフィールドの違いは、
 get() { プログラム }
void set(値を表す変数) { プログラム } 
があるところなんだ。
get()はプロパティの値を取得する部分で、set(値を表す変数)はプロパティの値を設定する部分なんだ。 setの部分だけ注意が必要で代入する値を宣言する必要があるんだ。 それにしても、何故C++/CLIは型を何度も宣言しなくちゃならないんだ? うっしししし、よーし試しにset()の型を変えてやるピヨー。
エラー	1	error C3901: 'get': 戻り値の型 'int' を指定しなければなりません	
えっ?コンパイルエラーが出たぞ。そりゃ当たり前といえば当たり前なんだけど、 何でコンパイラがわかっている事を一々書かなければならないんだ? ボクには理解できないピヨ。

コンパイラが分かっている事を書く必要ある?

これは余談なんだけど実はプロパティは全てのオブジェクト指向言語に実装されているわけではないんだ。 それは何故かというと、メソッドでも実現できるから「メソッドで同じことが出来るからいいじゃん」という人がいるからなんだ。 だけどボク個人の意見としてはリフレクションを有効に使うためにはプロパティとメソッドを明確に分けたほうが良いと思うピヨ。
余談おしまい。この記事もおしまい。

追記
これを書いた後でC++/CLIプロパティに凄い機能を発見したピヨ。
C++/CLIでは、省略記法というのがあって、サンプルコードのNameプロパティを、
property String^ Name;
で定義できるんだ!しかもフィールドも要らない。これは便利!他の言語にはないよ。文句言ってごめんC++/CLI。見直したよ。
別窓 | C++/CLI | コメント:0 | トラックバック:0 | ∧top | under∨
<<中の人の徒然草15 | 無差別に技術をついばむ鳥 | C++/CLIをつつく11−クラス。サブルーチンの進化>>

この記事のコメント

∧top | under∨

コメントの投稿

 

管理者だけに閲覧
 

この記事のトラックバック

∧top | under∨
| 無差別に技術をついばむ鳥 |