65.9K
CodeProject 正在变化。 阅读更多。
Home

为 .NET 包装 MFC 的列表类为 IEnumerable<T>

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.69/5 (3投票s)

2012年3月9日

CPOL
viewsIcon

15185

如何为 MFC 列表类创建一个尽可能精简的 .NET IEnumerable 包装器。

引言

在为非托管类创建包装器时,你可能会需要包装 MFC 集合,例如 CStringList。 一种方法(部分地)就是将它们包装为 IEnumerable<T>

背景

CStringList 使用 POSITION 结构来遍历列表。 基本上,一个“相当精简”的包装层,可以内部存储这个位置,并为外部世界提供一个友好的 IEnumerable 接口。

#pragma once

using namespace System;
using namespace System::Collections;

namespace MFCWrapper
{
    public ref class CStringListWrapper : Generic::IEnumerable<String^>
    {
    public:

        /// <summary>
        // not a very nice syntax, but this is how
        // explicit interface implementation is done in C++/CLI.
        /// IEnumerable(of T).GetEnumerator()
        /// </summary>
        virtual Generic::IEnumerator<String^>^ GetEnumerator() sealed = 
                Generic::IEnumerable<String^>::GetEnumerator
        { 
            return gcnew StringListEnumerator(this); 
        }

        /// <summary>
        // IEnumerable.GetEnumerator()
        /// </summary>
        virtual System::Collections::IEnumerator^ GetEnumeratorBase() sealed = 
                System::Collections::IEnumerable::GetEnumerator
        { 
            return GetEnumerator(); 
        }

        /// <summary>
        // Returns the pointer from the CStringList being wrapped
        /// </summary>
        CStringList * GetUnmanagedPointer()
        {
            return m_pStringList;
        }

        /// <summary>
        // Provides the wrapper with a (new) pointer to a CStringList
        /// </summary>
        void SetUnmanagedPointer(CStringList * pList)
        {
            if(m_pStringList)// might replace old one
            {
                delete m_pStringList;
                m_pStringList = NULL;
            }
            m_pStringList = pList;
        }

        /// <summary>
        // Insert at the beginning
        /// </summary>
        void AddHead(String ^ str)
        {
            if(str == nullptr)
                return;
            m_pStringList->AddHead((CString) str);
        }

        /// <summary>
        // Insert at the end
        /// </summary>
        // 
        void AddTail(String ^ str)
        {
            if(str == nullptr)
                return;
            m_pStringList->AddTail((CString) str);
        }

        /// <summary>
        // remove the first occurence and returns true if an item has been removed
        /// </summary>
        bool Remove(String ^ str)
        {
            if(str == nullptr)
                return false;

            CString strNative = (CString) str;
            POSITION pos = m_pStringList->Find(strNative);
            if(pos)
            {
                m_pStringList->RemoveAt(pos);
                return true;
            }
            return false;

        }

        /// <summary>
        // Removes all occurences of str
        /// </summary>
        int RemoveAll(String ^ str)
        {
            if(str == nullptr)
                return 0;

            int numRemoved = 0;
            CString strNative = (CString) str;

            POSITION pos = m_pStringList->Find(strNative);
            while(pos)
            {
                m_pStringList->RemoveAt(pos);
                ++numRemoved;
                pos = m_pStringList->Find(strNative);
            }

            return numRemoved;
        }

        /// <summary>
        // Clears list
        /// </summary>
        void Clear()
        {
            m_pStringList->RemoveAll();
        }

        /// <summary>
        // Gets number of elements
        /// </summary>
        property int Count
        {
            int get()
            {
                return m_pStringList->GetCount();
            }
        }

        CStringListWrapper(){ SetUnmanagedPointer(new CStringList()); }

    internal:
        CStringListWrapper(CStringList * pList){ SetUnmanagedPointer(pList); }
        !CStringListWrapper(void) { if(m_pStringList){ delete m_pStringList; m_pStringList = NULL; } }
        virtual ~CStringListWrapper(void){ this->!CStringListWrapper(); }

    private:
        ref struct StringListEnumerator : public Generic::IEnumerator<String^>
        {

        public:
            StringListEnumerator(CStringListWrapper ^ wrapper)
            {
                // Initialize position
                m_wrapper = wrapper;
                m_pos = m_wrapper->GetUnmanagedPointer()->GetHeadPosition();
                m_element = nullptr;
            }
            property String ^ Current
            { 
                virtual String^ get()
                {
                    return m_element;
                } 
            };

            property Object^ CurrentBase
            { 
                virtual Object^ get() sealed = System::Collections::IEnumerator::Current::get
                { 
                    return Current;
                } 
            };

            virtual bool MoveNext()
            { 
                // TODO: throw "Collection was modified; enumeration operation may not execute."
                // if list had been modified while iterating

                if(m_pos == NULL)
                    return false;

                POSITION curPos = m_pos;

                CString strCurrent = m_wrapper->GetUnmanagedPointer()->GetNext(curPos);
                m_element = gcnew String (strCurrent);

                m_pos = curPos;
                return true;
            }

            virtual void Reset()
            { 
                m_pos = m_wrapper->GetUnmanagedPointer()->GetHeadPosition();
            }

            virtual ~StringListEnumerator()
            {
            }

        private:
            String ^ m_element; // current element
            POSITION m_pos;     // current (unmanaged position) during iteration
            CStringListWrapper ^ m_wrapper;
        };
        CStringList * m_pStringList; // CStringList being wrapped
    };
}

使用代码

然后,你可以像这个 C# 示例程序演示的那样,遍历列表、添加/删除元素等。

using System.Diagnostics;

using MFCWrapper;

namespace MfcWrapperTest
{
    class Program
    {
        static void Main(string[] args)
        {
            CStringListWrapper wrapper = new CStringListWrapper();

            DumpList(wrapper);

            wrapper.AddHead("two");
            wrapper.AddTail("three");
            wrapper.AddHead("one");
            DumpList(wrapper); //one, two, three

            wrapper.AddTail("three");
            wrapper.AddTail("three");
            wrapper.AddTail("three");
            wrapper.RemoveAll("three");
            wrapper.Remove("two");
            DumpList(wrapper); // one

            wrapper.RemoveAll("one");
            wrapper.AddHead("five");
            wrapper.AddTail("six");
            wrapper.AddHead("four");
            DumpList(wrapper); // four, five, six

            wrapper.Clear();
            DumpList(wrapper); // (empty)


        }
        static void DumpList(CStringListWrapper myStringListWrapper)
        {
            foreach (string s in myStringListWrapper)
                Trace.WriteLine(s);
        }
    }
}

关注点

类似的包装也可以应用于

  • CPtrList,特别是如果列表中指针的类型已知,即指针本身可以被包装。 在这种情况下,你可以包装为 IEnumerable<T>
  • CObList。 同样,如果 List 内部对象的运行时类已知,则可以将 List 包装为 IEnumerable<T>
  • CMapStringToStringCMapStringToPtr 等:代码可以调整为将 CMap 类包装为 IDictionary<TKey, TValue>

对于列表类,可以考虑将其包装为 IList<T>,这基本上意味着额外实现 ICollection<T>

© . All rights reserved.