集合 (C++/CX)
在C++/CX 程式中,您可以自由使用標準範本程式庫 (STL) 容器,或任何其他使用者定義的集合類型。 但是,當您在 Windows 執行階段應用程式二進位介面 (ABI) 之間來回傳遞集合時 (例如,傳遞到 XAML 控制或 JavaScript 用戶端),您必須使用 Windows 執行階段集合類型。
Windows 執行階段會定義集合和相關類型的介面,C++/CX 會在collection.h 標頭檔中提供具體 C++實作。 下圖顯示集合型別之間的關聯性:
Platform::Collections::VectorView 類別和 Platform::Collections::MapView 類別是
Vector
和Map
的唯讀版本。迭代器在 Platform::Collections 命名空間中定義。 這些迭代器可以滿足 STL 迭代器的需求,並且可讓您針對任何 Windows::Foundation::Collections介面類型或 Platform::Collections具象類型使用 std::find 、 std::count_if 和其他 STL 演算法。 例如,這表示您可以逐一查看以 C# 建立的 Windows 執行階段元件中的集合,並套用 STL 演算法。
重要
Proxy 迭代器
VectorIterator
和VectorViewIterator
會利用 Proxy 物件VectoryProxy<T>
和ArrowProxy<T>
來與 STL 容器搭配使用。 如需詳細資訊,請參閱本文章稍後的<VectorProxy 元素>。C++/CX 集合類型支援 STL 容器支援的相同執行緒安全性保證。
Windows::Foundation::Collections::IObservableVector 和 Windows::Foundation::Collections::IObservableMap 會定義只要集合變更 (無論變更方式為何) 就會觸發的事件。 藉由實作這些介面,就能讓 Platform::Collections::Map 和 Platform::Collections::Vector 與 XAML 集合進行資料繫結。 例如,如果您具有資料繫結至
Vector
的Grid
,當您將項目加入至集合時,此變更會反映在 Grid UI。
向量用法
當您的類別必須將序列容器傳遞至另一個 Windows 執行階段元件時,請使用 Windows::Foundation::Collections:: IVector<T> 做為參數或傳回型別,而 Platform::Collections::Vector<T> 做為具體實作。 如果您嘗試在公用傳回值或參數中使用 Vector
類型,則會引發編譯器錯誤 C3986。 只要將 Vector
變更為 IVector
,就可以修正這個錯誤。
重要
如果您在自己的程式中傳遞序列,則使用 Vector
或 std::vector
,因為這些方法比 IVector
更有效率。 只有在透過 ABI 傳遞容器時,才應該使用 IVector
。
Windows 執行階段類型系統不支援不規則陣列的概念,因此您也就不能將 IVector<Platform::Array<T>>
做為傳回值或方法參數傳遞。 若要跨 ABI 傳遞不規則陣列或一組序列中的某個序列,請使用 IVector<IVector<T>^>
。
Vector<T>
提供在集合中新增、移除和存取項目的必要方法,此集合可隱含轉換為 IVector<T>
。 您也可以在 Vector<T>
的執行個體上使用 STL 演算法。 下列範例示範一些基本用法。 這裡的 begin 函式 和 end 函式 來自 Platform::Collections
命名空間,而非 std
命名空間。
#include <collection.h>
#include <algorithm>
using namespace Platform;
using namespace Platform::Collections;
using namespace Windows::Foundation::Collections;
void Class1::Test()
{
Vector<int>^ vec = ref new Vector<int>();
vec->Append(1);
vec->Append(2);
vec->Append(3);
vec->Append(4);
vec->Append(5);
auto it =
std::find(begin(vec), end(vec), 3);
int j = *it; //j = 3
int k = *(it + 1); //or it[1]
// Find a specified value.
unsigned int n;
bool found = vec->IndexOf(4, &n); //n = 3
// Get the value at the specified index.
n = vec->GetAt(4); // n = 3
// Insert an item.
// vec = 0, 1, 2, 3, 4, 5
vec->InsertAt(0, 0);
// Modify an item.
// vec = 0, 1, 2, 12, 4, 5,
vec->SetAt(3, 12);
// Remove an item.
//vec = 1, 2, 12, 4, 5
vec->RemoveAt(0);
// vec = 1, 2, 12, 4
vec->RemoveAtEnd();
// Get a read-only view into the vector.
IVectorView<int>^ view = vec->GetView();
}
如果您有現有使用 std::vector
的程式碼,而您想要在 Windows 執行階段元件中重複使用它,請使用其中一個 Vector
建構函式,當您透過 ABI 傳遞集合時,此建構函式接受 std::vector
或一組迭代器來建構 Vector
。 下列範例示範如何從 Vector
使用 std::vector
移動建構函式,以提高初始化效率。 在移動作業後,原始 vec
變數就不再有效。
//#include <collection.h>
//#include <vector>
//#include <utility> //for std::move
//using namespace Platform::Collections;
//using namespace Windows::Foundation::Collections;
//using namespace std;
IVector<int>^ Class1::GetInts()
{
vector<int> vec;
for(int i = 0; i < 10; i++)
{
vec.push_back(i);
}
// Implicit conversion to IVector
return ref new Vector<int>(std::move(vec));
}
如果您必須在未來透過 ABI 傳遞字串向量,您必須決定最初以 std::wstring
型別或 Platform::String^
型別建立字串。 如果您必須對字串進行許多處理,則使用 wstring
。 否則,請以 Platform::String^
類型建立字串,以避免稍後進行轉換的成本。 您也必須決定是否要將這些字串放入 std:vector
或 Platform::Collections::Vector
內部。 一般的作法是,只有在您透過 ABI 傳遞容器時,才使用 std::vector
,然後從中建立 Platform::Vector
。
Vector 中的實值類型
要儲存在 Platform::Collections::Vector 中的所有元素都必須隱含或透過您提供的自訂 std::equal_to 比較子支援相等比較。 所有參考型別及所有純量型別都隱含支援相等比較。 針對非純量實值類型 (例如 Windows::Foundation::DateTime) 或自訂比較 (例如 objA->UniqueID == objB->UniqueID
),您必須提供自訂函式物件。
VectorProxy 元素
Platform::Collections::VectorIterator 和 Platform::Collections::VectorViewIterator 可讓您搭配 IVector<T> 容器使用 range for
迴圈和 std::sort 等演算法。 但是,您無法透過 C++ 指標取值存取 IVector
項目,只能透過 GetAt 和 SetAt 方法存取。 因此,基於「標準程式庫」的要求,這些迭代器會使用 Proxy 類別 Platform::Details::VectorProxy<T>
和 Platform::Details::ArrowProxy<T>
透過 *、> 和 [] 運算子存取個別元素。 嚴格來說,如果指定 IVector<Person^> vec
,則 *begin(vec)
的類型就是 VectorProxy<Person^>
。 不過,對您的程式碼來說,Proxy 物件永遠都像是不存在一樣。 這些 Proxy 物件沒有記錄在文件中,因為它們僅供迭代器在內部使用,但是了解機制如何運作還是很重要。
當您針對 IVector
容器使用基於範圍的 for
迴圈時,請使用 auto&&
讓迭代器變數能夠正確繫結至 VectorProxy
元素。 如果您使用 auto&
,就會引發編譯器警告 C4239,而且警告文字中會提及 VectoryProxy
。
下圖顯示針對 range for
執行的 IVector<Person^>
迴圈。 請注意,程式會執行到第 64 行的中斷點停止。 [ 快速監看式 ] 視窗會顯示迭代器變數 p
就是擁有 VectorProxy<Person^>
和 m_v
成員變數的 m_i
。 但是,當您針對這個變數呼叫 GetType
時,它會將相同的類型傳回給 Person
執行個體 p2
。 因此,雖然 VectorProxy
和 ArrowProxy
可能會出現在 [ 快速監看式]、偵錯工具、某些編譯器錯誤或其他位置,但是一般而言,您仍然不必明確為它們撰寫程式碼。
有一種情況下您必須為 Proxy 物件撰寫程式碼,就是必須針對元素執行 dynamic_cast
的時候 (例如,當您在 UIElement
項目集合中尋找特定類型的 XAML 物件時)。 在這種情況下,您必須先將這個項目轉換為 Platform::Object^,然後再執行動態轉換:
void FindButton(UIElementCollection^ col)
{
// Use auto&& to avoid warning C4239
for (auto&& elem : col)
{
Button^ temp = dynamic_cast<Button^>(static_cast<Object^>(elem));
if (nullptr != temp)
{
// Use temp...
}
}
}
對應用法
這個範例示範如何插入項目及在 Platform::Collections::Map中查閱項目,然後傳回 Map
作為唯讀 Windows::Foundation::Collections::IMapView 類型。
//#include <collection.h>
//using namespace Platform::Collections;
//using namespace Windows::Foundation::Collections;
IMapView<String^, int>^ Class1::MapTest()
{
Map<String^, int>^ m = ref new Map<String^, int >();
m->Insert("Mike", 0);
m->Insert("Dave", 1);
m->Insert("Doug", 2);
m->Insert("Nikki", 3);
m->Insert("Kayley", 4);
m->Insert("Alex", 5);
m->Insert("Spencer", 6);
// PC::Map does not support [] operator
int i = m->Lookup("Doug");
return m->GetView();
}
一般而言,基於效能考量,在內部對應功能上最好使用 std::map
型別。 如果您必須透過 ABI 傳遞容器,請從 std::map 建構 Platform::Collections::Map ,然後傳回 Map
作為 Windows::Foundation::Collections::IMap。 如果您嘗試在公用傳回值或參數中使用 Map
類型,則會引發編譯器錯誤 C3986。 只要將 Map
變更為 IMap
,就可以修正這個錯誤。 在某些情況下,例如,如果不是進行大量搜尋或插入,且您經常透過 ABI 傳遞集合,則一開始便使用 Platform::Collections::Map
可耗費較少資源,還能避免轉換 std::map
時的成本。 在任何情況下,請避免在 IMap
上執行查閱和插入作業,因為這些是效能最低的三個類型。 只有在透過 ABI 傳遞容器時,才應該轉換為 IMap
。
Map 中的實值類型
在 Platform::Collections::Map 中的項目已排序。 要儲存在 Map
中的所有項目都必須隱含或透過您提供的自訂 stl::less 比較子,支援使用嚴格弱式順序的小於比較。 純量類型隱含支援此比較。 針對非純量實值類型 (例如 Windows::Foundation::DateTime
) 或自訂比較 (例如 objA->UniqueID < objB->UniqueID
),您必須提供自訂比較子。
集合類型
集合分成四種分類:序列集合和關聯集合的可修改版本和唯讀版本。 此外,C++/CX 提供三個迭代器類別來簡化集合的存取,藉此增強集合。
您可以變更可修改的集合的元素,但只能讀取唯讀集合的元素,又稱為 檢視 (View)。 Platform::Collections::Vector 或 Platform::Collections::VectorView 集合的元素,可以使用迭代器或該集合的 Vector::GetAt 和索引來存取。 您可以使用集合的 Map::Lookup 和索引鍵來存取關聯集合的元素。
Platform::Collections::Map 類別
可修改的關聯集合。 對應元素是機碼值組。 支援查閱機碼以擷取其關聯值與逐一查看所有機碼值組。
Map
和 MapView
以 <K, V, C = std::less<K>>
為樣板,因此,您可以自訂比較子。 此外, Vector
和 VectorView
以 <T, E = std::equal_to<T>>
為樣板,因此,您可以自訂 IndexOf()
的行為。 這對於值結構的 Vector
和 VectorView
而言十分重要。 例如,若要建立向量 <Windows::Foundation::DateTime>,您必須提供自訂比較子,因為 DateTime 不會多載 == 運算子。
Platform::Collections::MapView 類別
Map
的唯讀版本。
Platform::Collections::Vector 類別
可修改的序列集合。 Vector<T>
支援固定時間隨機存取和分期固定時間 Append 作業。
Platform::Collections::VectorView 類別
Vector
的唯讀版本。
Platform::Collections::InputIterator 類別
滿足 STL 輸入迭代器需求的 STL 迭代器。
Platform::Collections::VectorIterator 類別
滿足 STL 可變動隨機存取迭代器需求的 STL 迭代器。
Platform::Collections::VectorViewIterator 類別
滿足 STL const
隨機存取迭代器需求的 STL 迭代器。
begin() 和 end() 函式
為了簡化 STL 來處理 Vector
、VectorView
、Map
、MapView
,和任意 Windows::Foundation::Collections
物件的用法,C++/CX 支援 begin Function 和 end Function 非成員函式的多載。
下表列出可用的迭代器和函式。
集合變更事件
Vector
和 Map
藉由實作變更或重設集合物件時,或者插入、移除或變更集合的任何元素時所發生的事件,來支援 XAML 集合中的資料繫結。 您可以撰寫自己的型別來支援資料繫結,但是您無法繼承自 Map
或 Vector
,因為這些型別是密封型別。
Windows::Foundation::Collections::VectorChangedEventHandler 和 Windows::Foundation::Collections::MapChangedEventHandler 委派會為集合變更事件的事件處理常式指定簽章。 Windows::Foundation::Collections::CollectionChange 公用列舉類別及 Platform::Collection::Details::MapChangedEventArgs
和 Platform::Collections::Details::VectorChangedEventArgs
ref 類別會儲存事件引數,以判斷造成事件的原因。 *EventArgs
類型在 Details
命名空間中定義,因為當您使用 Map
或 Vector
時,不需要明確建構或使用這些類型。