用Enum的时候,可能会有要遍历Enum中所有已定义值的功能。而当前各种.NET语言都没有提供对此需求的语法支持。我们只能用Enum.GetValues来实现这个任务。但是,Enum.GetValues一来是要用到运行时类型信息,让人不爽;二来得到的数组不直接是所需枚举类型的,需要转换。最好有一种强类型的语法可以帮助我们做这件事。所以,我就写了这个简单的小程序。其实内部还是得用Enum.GetValues,但是只在第一次使用时调用,后面就将这个取得的值集合保存起来,即使多次使用也不会担心性能受损。这个程序用C++/CLI BETA1写成,不适用于C++/CLI CTP或Tools Update。用C++的原因是,C#和VB.NET都对泛型约束语法做了过分的限制,让我写不了这个程序……

首先是ValueCollection类:

generic<typename T> where T : System::Enum
[DefaultMember("Item")] //让Item属性成为默认属性(C#中的索引器)
public ref class EnumValueCollection : public IEnumerable<T>
{
    private:
    initonly array<T>^ values;
    IEnumerator<T>^ enumerator;

    protected:
    virtual
IEnumerator<T>^ GetValuesEnumerator() = IEnumerable<T>::GetEnumerator
    {
        if (!enumerator)
        {
            enumerator = ((IEnumerable<T>^)values)->GetEnumerator();
        }

        return enumerator;
    }

    private public: // internal: in future version
    EnumValueCollection(void)
    {
        values = (array<T>^)Enum::GetValues(typeid<T>);
    }
    public:
    property
T Item[] //参数化属性
    {
        T get(int index)
        {
            try
            {
                return values[index];
            }
            catch(Exception^ e)
            {
                throw gcnew ArgumentOutOfRangeException("index",
                    "This enum does not contain a value of this index");
            }
        }
    }
};

接下来要有一个维护ValueCollection实例Singleton的帮助类:

generic<typename T> where T : System::Enum
public ref class EnumHelper sealed
{
    private:
    EnumHelper()
    {
        //不能创建此类的实例
    }

    static EnumValueCollection<T>^ valueCol;
    initonly static Object^ syncRoot = gcnew Object();
    public:

    static property EnumValueCollection<T>^ ValueCollection
    {
        EnumValueCollection<T>^ get()
        {
            if (!valueCol)
            {
                try
                {
                    Monitor::Enter(syncRoot);
                    if (!valueCol)
                    {
                        valueCol = gcnew EnumValueCollection<T>();
                    }
                }
                finally
                {
                    Monitor::Exit(syncRoot);
                }
            }

            return valueCol;
        }
    }

};

很简单,不用多解释了。这是用法:[Visual Basic]

For Each o As MessageBoxButtons In EnumHelper(Of MessageBoxButtons).ValueCollection
    TextBox1.AppendText(o.ToString & vbCrLf)
Next

Dim x As MergeAction = EnumHelper(Of MergeAction).ValueCollection(1)

MsgBox(x.ToString)