C++/CLIで、genericなCollectionを自力で実装してみる
.NET Frameworkには、Collection
ClearItems InsertItem RemoveItem SetItem
の4つのメソッドさえ実装すれば良いので、.NET 1.X時代のように大変な思いをしてCollectionを書く必要はない。非常に楽だ。
しかしながら、コレクションの値を参照されたときに初めて値を用意するといったLazy Load的な処理をしようとすると、このクラスは全く頼りにならない。読み込み方向のプロパティ(Item/default)が、
virtual property T default [int] { T get (int index) sealed; void set (int index, T value) sealed; }
という風に定義されているからだ。くせ者なのはsealed。そうじゃなくても、基底クラスにキャストされると中が直接見えるのはちょっと不味い。
ということで、頼るモノがなくなってしまい、全部を自力で実装しなくてはならないことになる。MSDNライブラリを参照すると、Collection
[SerializableAttribute] [ComVisibleAttribute(false)] generic<typename T> public ref class Collection : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
という具合になっており、一見すると、実装しないといけないメソッドが死ぬほどありそうな予感。しかしながら、実は、IList
ということで、とりあえずは、List
generic<typename T> public ref class CollectionImpl : public System::Collections::Generic::IList<T>, public System::IDisposable { public: CollectionImpl() { m_list = gcnew System::Collections::Generic::List<T>(); } virtual ~CollectionImpl() { } !CollectionImpl() { } // IList<T> property T default[int] { virtual T get(int index) { return m_list[index]; } virtual void set(int index, T value) { m_list[index] = value; } } virtual int IndexOf(T item) { return m_list->IndexOf(item); } virtual void Insert(int index, T item) { return m_list->Insert(index, item); } virtual void RemoveAt(int index) { return m_list->RemoveAt(index); } // ICollection<T> property int Count { virtual int get() { return m_list->Count; } } property bool IsReadOnly { virtual bool get() { return false; // we provides RW array } } virtual void Add(T item) { return m_list->Add(item); } virtual void Clear() { return m_list->Clear(); } virtual bool Contains(T item) { return m_list->Contains(item); } virtual void CopyTo(array<T>^ arr, int arrIndex) { m_list->CopyTo(arr, arrIndex); } virtual bool Remove(T item) { return m_list->Remove(item); } private: ref class CI_Enumerator sealed : public System::Collections::Generic::IEnumerator<T> { public: CI_Enumerator(CollectionImpl<T>^ col) { m_enum = col->m_list->GetEnumerator(); } virtual ~CI_Enumerator() { delete m_enum; m_enum = nullptr; } !CI_Enumerator() { } property T Current { virtual T get() = System::Collections::Generic::IEnumerator<T>::Current::get { return m_enum->Current; } } virtual bool MoveNext() { return m_enum->MoveNext(); } virtual void Reset() { return m_enum->Reset(); } protected: virtual property Object^ Current_NoGeneric { // overrides non-generic version of IEnumerator::Current virtual Object^ get() = System::Collections::IEnumerator::Current::get { return Current; } } private: System::Collections::Generic::IEnumerator<T>^ m_enum; }; public: // IEnumerable<T> virtual System::Collections::Generic::IEnumerator<T>^ GetEnumerator() { return m_list->GetEnumerator(); } protected: // IEnumerable virtual System::Collections::IEnumerator^ GetEnumerator_NoGeneric() = System::Collections::IEnumerable::GetEnumerator { return m_list->GetEnumerator(); } private: System::Collections::Generic::List<T>^ m_list; };
ちなみに、IDisposableを実装しているのは、僕の場合には、実は中身がstd::vectorになる予定だから。List