4

Below is a piece of code I find useful in that I can use it to nip round enums quickly. CurrentEnum stores a reference to the enum that was used to provide the strings, in this case "Bald" and it can change.

What I am trying to do is replicate the same functionality in Silverlight which does not have a GetEnumValues function. The preferable solution would be an extension method that can be used in the same way as in my example below.

class Program
{
    enum Cats { Fluffy, Furry, Bald };
    enum Dogs { Big, Fat, Ugly };

    static Type CurrentEnum = typeof(Cats);

    static void Main(string[] args)
    {
        Int32 i = (Int32)Enum.Parse(CurrentEnum, "Bald", true);
        i = i == CurrentEnum.GetEnumValues().Length - 1 ? 0 : i + 1;
        String nextValue = CurrentEnum.GetEnumValues().GetValue(i).ToString();

        Console.WriteLine(nextValue);
        Console.ReadKey();
    }
}

UPDATE:

Here is what I have decided on for now:

public static class Extensions
{
    public static Array GetEnumValues(this Type enumType)
    {
        if (enumType == null) throw new ArgumentException("Type 'enumType' cannot be null");
        if (!enumType.IsEnum) throw new ArgumentException("Type '" + enumType.Name + "' is not an enum");

        Int32 i = 0;
        Boolean bDefined = true;
        List<String> list = new List<String>();

        do
        {
            if (Enum.IsDefined(enumType, i))
            {                    
                list.Add(Enum.GetName(enumType, i));
                ++i;
            }
            else
            {
                bDefined = false;
            }
        }
        while (bDefined);

        return list.ToArray();
    }
}
descf
  • 1,302
  • 1
  • 12
  • 31
  • 3
    It would really help if you told us what didn't work in [this question](http://stackoverflow.com/questions/1038234) so we can jump right to helping you rather than wonder about a possible duplicate. – Tim Post Aug 15 '11 at 07:16
  • I had already done that in response to comments that have disappeared. As noted in the the comments on that question the solutions do not work in the same way as in .NET and so they do not work with the CurrentEnum variable in my example. My question is very specific and my example is the test. – descf Aug 15 '11 at 07:28
  • I'll admit I ignored that one because the format of the answer didn't look right to me and it only got 1 vote. Perhaps I should give it a closer look. – descf Aug 15 '11 at 08:01
  • I had an error in my initial code such that I didn't replace the second GetEnumValues() when testing in non-Silverlight .NET; not sure how I missed it, but that's why I didn't realize I was incorrect when I said that the function I mentioned was equivalent. It clearly was not. Hopefully resolved now, and I'm removing my prior incorrect comments so they don't cause confusion. My new answer has what I believe to be fully .NET equivalent versions, tested in, and with a solution provided for, Silverlight 3. – shelleybutterfly Aug 15 '11 at 19:59

3 Answers3

4

If I had to guess (untested):

    public static Array GetValues(Type type)
    {
        var fields = type.GetFields(BindingFlags.Static | BindingFlags.Public);
        Array result = Array.CreateInstance(type, fields.Length);
        for(int i = 0 ; i < fields.Length ; i++)
        {
            result.SetValue(Enum.ToObject(type, fields[i].GetValue(null)), i);
        }
        return result;
    }

Note that I don't attempt to address the ordering here; the order of fields is explicitly not defined. So use this to get the values in no specific order.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
3

There is another question similar to this one at Iterating through an enumeration in Silverlight? but none of the answers there provides an implementation that exactly replicates the way GetEnumValues() (and GetEnumNames()) work. I used one of the answers from that question to create a solution which should give you what you need; I believe this exactly duplicates the .NET functionality of those functions in Silverlight, and I have uploaded the entire test project for Silverlight that I used to create the functions and test the usage: GetEnumValuesSilverlightImpl-StackOverflow-7062208.zip.

The application itself is a single form with a single button and text area. Clicking the button iterates through each of three values of four different enumerations, and logs those values to the form. It should be easy enough to add extra test cases, although be aware that the text area does not word wrap, so that will be needed to be accounted for if one wishes to put values wider than the text area. I tested the original poster's test case, writing out all three values for the two original enums, writing out all three values for an enum I created that had three non contiguous values, and writing out all three values for a [Flag] enum I created that had three different flag values.

The relevant code:

EnumValuesExtensions.cs: extensions class containing function calls

using System;
using System.Linq;
using System.Reflection;

namespace SilverlightApplication1
{
    public static class EnumValuesExtensions
    {
        public static Array GetEnumValues(this Type EnumType)
        {
            if (!EnumType.IsEnum)
                throw new ArgumentException("GetEnumValues: Type '" + EnumType.Name + "' is not an enum");

            return
                (
                  from field in EnumType.GetFields(BindingFlags.Public | BindingFlags.Static)
                  where field.IsLiteral
                  select (object)field.GetValue(null)
                )
                .ToArray();
        }

        public static string[] GetEnumNames(this Type EnumType)
        {
            if (!EnumType.IsEnum)
                throw new ArgumentException("GetEnumNames: Type '" + EnumType.Name + "' is not an enum");

            return
                (
                  from field in EnumType.GetFields(BindingFlags.Public | BindingFlags.Static)
                  where field.IsLiteral
                  select field.Name
                )
                .ToArray();
        }
    }
}

MainPage.xaml.cs: Form source containing Test Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.Reflection;

namespace SilverlightApplication1
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }

        [Flags]
        enum Insects { Creepy = 0x0001, Creepier = 0x0002, Creepiest = 0x0004 };

        enum Cats { Fluffy, Furry, Bald };
        enum Dogs { Big, Fat, Ugly };
        enum Rodents { Cute = 3, Cuter = 7, Cutest = 32 };

        static Type CurrentEnum = typeof(Cats);

        private void btnRunTests_Click(object sender, RoutedEventArgs e)
        {
            // from original test code
            Int32 i = (Int32)Enum.Parse(CurrentEnum, "Bald", true);

            i = i == CurrentEnum.GetEnumValues().Length - 1 ? 0 : i + 1;
            String nextValue = CurrentEnum.GetEnumValues().GetValue(i).ToString();

            textBlock1.Text += nextValue + Environment.NewLine;

            // new test code
            LogEnum(typeof(Cats));
            LogEnum(typeof(Dogs));
            LogEnum(typeof(Rodents));
            LogEnum(typeof(Insects));
        }

        public void LogEnum(Type T)
        {
            Array CurrentEnumValues;
            CurrentEnumValues = T.GetEnumValues();

            for (int i=0; i < CurrentEnumValues.Length; ++i)
            {
                string EnumName = CurrentEnumValues.GetValue(i).ToString();
                int EnumValue = (int)CurrentEnumValues.GetValue(i);
                textBlock1.Text += "[" + EnumName + " = " + EnumValue.ToString() + "], ";
            }

            textBlock1.Text += Environment.NewLine;
        }
    }
}

MainPage.xaml: The XAML for the Test Form

<UserControl x:Class="SilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="430" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <TextBlock Height="377" HorizontalAlignment="Left" Margin="12,41,0,0" Name="textBlock1" Text="" VerticalAlignment="Top" Width="376" />
        <Button Content="Run Tests" Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="btnRunTests" VerticalAlignment="Top" Width="75" Click="btnRunTests_Click" />
    </Grid>
</UserControl>

These tests are running in Silverlight 3, and the GetEnumValues() extension does appear to work identically to the .NET GetEnumValues() function, as it gives enum names when .ToString() is called, and the proper integer values when casted to (int).

I have not tested the GetEnumNames() extension, but it fairly clearly works by analogy to the GetEnumValues() extension, but if there are any issues I will be happy to take a look.

Thanks to ptoinson and Shimmy for their answer to a similar question, which formed the basis for the functions I wrote to answer this question.

Community
  • 1
  • 1
shelleybutterfly
  • 3,216
  • 15
  • 32
  • I think you need to explain a bit more about how you got it to return 'fluffy'. At the moment it looks like you're just pulling together a list of ints? – descf Aug 15 '11 at 14:22
  • Are you testing this in Silverlight? The function returns a list of ints - how do you find 'fluffy' in a list of ints? – descf Aug 15 '11 at 16:23
  • No, I wasn't, and I ended up missing the second necessary replacement in the code above that would have pointed out to me the thing about the List. It's not equivalent to the Type.GetEnumValues() call because that call returns an actual Array of the enums, so that's why you don't have to call GetEnumNames() in your code--using .ToString() casts it to the enum name string, but casting to int gives you the value. And, the code in the function given did not give the string portion, it just said you could get it. I have a version that implements equivalents now, and am updating the answer. – shelleybutterfly Aug 15 '11 at 19:05
0

There are two threads on this and I couldn't get anything to work. After fumbling around for a day I finally ended up with this:

    public static Array GetEnumValues(this Type enumType)
    {
        List<int> enumerations = new List<int>();
        FieldInfo[] fields = enumType.GetFields(BindingFlags.Static | BindingFlags.Public);
        object enumObj = enumType.GetType();

        foreach (FieldInfo fieldInfo in fields)
        {
            enumerations.Add((int)fieldInfo.GetValue(enumObj));
        }
        return enumerations.ToArray();
    }