屬性是C++/CLI的類成員,它類似于成員變量,但實際上不是。其主要區(qū)別在于,字段名引用了某個存儲單元,而屬性名則是調(diào)用某個函數(shù)。屬性擁有訪問屬性的set()和get()函數(shù)。當(dāng)我們使用屬性名時,實際上在調(diào)用該函數(shù)的get()或set()函數(shù)。如果一個屬性僅提供了get()函數(shù),則它是只讀屬性;如果一個屬性僅提供set()函數(shù),則它是只寫屬性。
類可以有2種不同的屬性:標(biāo)量屬性和索引屬性。標(biāo)量屬性是指通過屬性名來訪問的單值;索引屬性是利用屬性名加方框號來訪問的一組值。如 String類,其Length屬性為標(biāo)量屬性,用object->Length來訪問其長度,且Length是個只讀屬性。String還包含了索引屬性,可以用object[idx]來訪問字符串中第idx+1個字符。
屬性可以與類的實例(類對象)相關(guān),此時屬性被稱為實例屬性,如String類的Length屬性;如果用static修飾符指定屬性,則屬性為類屬性,所有該類得實例都具有相同的屬性值。
一、標(biāo)量屬性
標(biāo)量屬性是單值,用property關(guān)鍵字可定義標(biāo)量屬性,還需要定義其get()和set()函數(shù),如下例所示
value class Height
{
private:
// Records the height in feet and inches
int feet;
int inches;
literal int inchesPerFoot = 12;
literal double inchesToMeter = 2.54/100;
public:
// Create a height from inches value
Height(int ins)
{
feet = ins/inchesPerFoot;
inches = ins%inchesPerFoot;
}
// Create a height from feet and inches
Height(int ft, int ins) : feet(ft), inches(ins) {};
// The height in meters as a property
property double meters
{
double get()
{
return inchesToMeters * (feet*inchesPerFoot + inches);
}
}
// Create a string representation of the object
virtual String^ ToString() overrides
{
return feet + L" feet " + inches + L" inches";
}
};
上面的例子定義了一個merters的屬性,下面是屬性的用法
Height ht = Height(6, 8);
Console::WriteLine(L"The height is {0} meters", ht->meters);
屬性不一定要定義成內(nèi)聯(lián)函數(shù),也可以在.cpp中外部定義它,如在上例的定義中僅保留get()函數(shù)聲明
property double meters
{
double get();
}
函數(shù)定義在.cpp中時,需要加類名和函數(shù)名的限定(但不需要返回值?),方法如下:
Height::meters::get()
{
return inchesToMeters*(feet*inchesPerFoot+inches);
}
如果定義一個屬性時,不提供get()和set()函數(shù)定義,這種屬性被稱為平凡標(biāo)量屬性。對于此類屬性,編譯器將提供一個默認(rèn)的get()和set()實現(xiàn),如下例所示:
value class Point
{
public:
property int x;
proterty int y;
virtual String^ ToString() overrides
{
return L"("+x+L","+y+")"; // Result is (x,y)
}
};
下面是一個完整的例子,說明了標(biāo)量屬性的聲明及其使用方法
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::開始==>> [Ex7_16.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ex7_16.cpp : main project file.
#include "stdafx.h"
using namespace System;
// Class defining a person's height
value class Height
{
private:
// Record the height in feet and inches
int feet;
int inches;
literal int inchesPerFoot = 12;
literal double inchesToMeters = 2.54/100;
public:
// Create a height from inches value
Height(int ins)
{
feet = ins/inchesPerFoot;
inches = ins%inchesPerFoot;
}
// Create a height from feet and inches
Height(int ft, int ins) : feet(ft), inches(ins) {};
// The height in meters
property double meters
{
double get()
{
return inchesToMeters*(feet*inchesPerFoot+inches);
}
}
// Create a string representation of the object
virtual String^ ToString() override
{
return feet + L" feet " + inches + L" inches";
}
};
// Class defining a person's weight
value class Weight
{
private:
int lbs;
int oz;
literal int ouncesPerPound = 16;
literal double lbsToKg = 1.0/2.2;
public:
Weight(int pounds, int ounces)
{
lbs = pounds;
oz = ounces;
}
property int pounds
{
int get() { return lbs; }
void set(int value) { lbs = value; }
}
property int ounces
{
int get() { return oz; }
void set(int value) { oz = value; }
}
property double kilograms
{
double get() { return lbsToKg*(lbs+oz/ouncesPerPound); }
}
virtual String^ ToString() override
{
return lbs + L" pounds " + oz + L" ounces";
}
};
ref class Person
{
private:
Height ht;
Weight wt;
public:
property String^ Name;
Person(String^ name, Height h, Weight w) : ht(h), wt(w)
{
Name = name;
}
Height getHeight() { return ht; }
Weight getWeight() { return wt; }
};
int main(array<System::String ^> ^args)
{
Weight hisWeight = Weight(185, 7);
Height hisHeight = Height(6, 3);
Person^ him = gcnew Person(L"Fred", hisHeight, hisWeight);
Weight herWeight = Weight(105, 3);
Height herHeight = Height(5, 2);
Person^ her = gcnew Person(L"Freda", herHeight, herWeight);
Console::WriteLine(L"She is {0}", her->Name);
Console::WriteLine(L"Her weight is {0:F2} kilograms.", her->getWeight().kilograms);
Console::WriteLine(L"Her height is {0} which is {1:F2} meters.", her->getHeight(),
her->getHeight().meters);
Console::WriteLine(L"He is {0}", him->Name);
Console::WriteLine(L"His weight is {0}", him->getWeight());
Console::WriteLine(L"His height is {0} which is {1:F2} meters.", him->getHeight(),
him->getHeight().meters);
return 0;
}
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::結(jié)束==>> [Ex7_16.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
輸出為
She is Freda
Her weight is 47.73 kilograms.
Her height is 5 feet 2 inches which is 1.57 meters.
He is Fred
His weight is 185 pounds 7 ounces
His height is 6 feet 3 inches which is 1.91 meters.
二、索引屬性
索引屬性是類的一組屬性值,其訪問方法同數(shù)組元素那樣,在方括號內(nèi)加索引值來訪問。如果在方括號前面的是類對象的名稱,則該索引屬性被稱為默認(rèn)索引屬性(如String^ obj可以用obj[idx]來訪問字符串中第idx+1個字符),如果用屬性名[idx]來訪問索引屬性值,則稱為有名索引屬性。下面的代碼在類Name中定義了一個默認(rèn)索引屬性,
ref class Name
{
private:
array<String^>^ Names;
public:
Name(...array<String^>^ names) : Names(names) {}
// Indexed property to return any name
property String^ default[int]
{
// Retrieve indexed property value
String^ get(int index)
{
if(index >= Names->Length)
throw gcnew Exception(L"Index out of range");
return Names[index];
}
}
};
在上面的例子中,如果將default換成別的名字,則該屬性就成為一個有名索引屬性。在定義索引屬性時,方括號內(nèi)用int指定了索引的數(shù)據(jù)類型為int型,它也可以是別的數(shù)據(jù)類型。訪問索引屬性的get()函數(shù)的形參其數(shù)據(jù)類型必須與屬性名后方括號內(nèi)類型相同;set()函數(shù)必須有2個形參,第一個指定索引,第二個指定屬性元素的值。
下面是一個完整的例子,說明了索引屬性的定義與使用方法。
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::開始==>> [Ex7_17.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ex7_17.cpp : main project file.
#include "stdafx.h"
using namespace System;
ref class Name
{
private:
array<String ^>^ Names;
public:
Name(...array<String ^>^ names) : Names(names) {}
// Scalar property specifying number of names
property int NameCount
{
int get() { return Names->Length; }
}
// Indexed property to return names
property String^ default[int]
{
String ^ get(int index)
{
if(index >= Names->Length)
throw gcnew Exception(L"Index out of range");
return Names[index];
}
}
};
int main(array<System::String ^> ^args)
{
Name^ myName = gcnew Name(L"Ebenezer", L"Isaiah", L"Ezra", L"Inigo", L"Whelkwhistle");
// List the names
for(int i=0; i<myName->NameCount; i++)
Console::WriteLine(L"Name {0} is {1}", i+1, myName[i]);
return 0;
}
- - - - - - - - - - - - - - - - <<== 華麗的分割線 ::結(jié)束==>> [Ex7_17.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
輸出為
Name 1 is Ebenezer
Name 2 is Isaiah
Name 3 is Ezra
Name 4 is Inigo
Name 5 is Whelkwhistle
索引屬性的索引也可以不是整型,甚至可以不是數(shù)字,下面的例子定義了一個商店類,其屬性O(shè)pening指定了商店的開門時間,訪問該屬性的索引有2個參數(shù),如下面的例子所示
enum class Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };
// class defining a shop
ref class Shop
{
public:
property String^ Opening[Day, String^] // Opening times
{
String ^ get(Day day, String^ AmOrPm)
{
switch(day)
{
case Day::Saturday:
if(AmOrPm == L"am")
return L"9:00";
else
return L"14:30";
break;
case Day::Sunday:
return L"closed";
break;
default:
if(AmOrPm == L"am")
return L"9:30";
else
return L"14:00";
break;
}
}
}
};
使用該類的方法如下
Shop^ shop = gcnew Shop;
Console::WriteLine(shop->Opening(Day::Saturday, L"pm");
三、靜態(tài)屬性
靜態(tài)屬性為類的所有實例共有,類似于類的靜態(tài)成員變量。通過在屬性定義前添加修飾符static來定義,如下面的例子所示
value class Length
{
public:
static property String ^ Units
{
String ^ get() { return L"feet and inches"; }
}
};
無論是否創(chuàng)建類實例,靜態(tài)屬性都存在。如果已經(jīng)定義了類實例,則可以用實例名.屬性名來訪問靜態(tài)屬性。對于上面的例子如果已經(jīng)定義了一個類對象len,則可以如此訪問其靜態(tài)屬性:
Console::WriteLine(L"Class units are {0}.", len.Units);
注意:在定義了屬性之后,對應(yīng)的get_屬性名和set_屬性名自動成為系統(tǒng)保留名稱,不能為其它目的而使用他們。如果定義了默認(rèn)索引屬性,則set_Item和get_Item也成為系統(tǒng)保留名稱,不可被使用。