Click here to Skip to main content
12,355,066 members (60,489 online)
Click here to Skip to main content
Add your own
alternative version

Stats

7.6K views
4 downloads
12 bookmarked
Posted

One More C# Enum Enhancement

, 21 Jun 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
The tip describes a way to create C# enhanced enumerations that can contain class objects.

Introduction

An enum in C# is a value type used to represent a set of fixed distinct values of integer type such as byte, sbyte, short, ushort, int, uint, long or ulong. An article suggests the helper class set to create enum - like classes that can contain objects of C# classes.

Background

There are some articles on this subject, for example - here. This article proposes a bit different and a bit more "safe" way of creating and using enhanced enumerations.

The Main Point

The key point of the solution is: the constructor of the class of "enum" value should be the private one. In this case, we cannot create the object of the class with the help of the "new" operator. The object can be created either with the help of C# reflection, or with the help of the helper class method (the helper class uses the reflection also).

Using the Code

The class EnumValueHelper has one essential method - CreateObject, which creates the object of our underlying "enum data" class with a private constructor and initializes its public variables using C# reflection. Please note, what the order of the parameters at CreateObject method call should be the same as the order of the parameter declaration in the underlying "enum value" class (EnumLikeClassValue in this sample) below.

public abstract class EnumValueHelper<T> where T : class
{
    protected static T CreateObject(params object[] args)
    {
        var type = typeof (T);
        Type[] paramTypes = {}; // T has parametrless ctor

        var constructorInfo = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic,
            null, paramTypes, null);

        var obj = (T) constructorInfo.Invoke(new object[] {}); // T has parametrless ctor

        var fieldInfoArray = type.GetFields(BindingFlags.Instance | BindingFlags.Public);

        if (fieldInfoArray.Length != args.Length)
        {
            throw new ArgumentException((typeof (EnumValueHelper<T>)).FullName +
                                        " CreateObject: wrong input paramerter count");
        }

        for (var i = 0; i < args.Length; i++)
        {
            try
            {
                if (args[i].GetType() != fieldInfoArray[i].FieldType)
                {
                    throw new ArgumentException((typeof (EnumValueHelper<T>)).FullName +
                                                " CreateObject: wrong input paramerter type for position " + i);
                }

                fieldInfoArray[i].SetValue(obj, args[i]);
            }
            catch (Exception ex)
            {
                throw new Exception((typeof (EnumValueHelper<T>)).FullName +
                                    " CreateObject: unknown failure for position " + i + " message =  " + ex.Message);
            }
        }

        return obj;
    }

    public override string ToString()
    {
        var fiaBase = GetType().GetFields(BindingFlags.Instance | BindingFlags.Public);
        var sb = new StringBuilder();
        for (int i = 0; i < fiaBase.Count(); i++)
        {
            sb.Append(fiaBase[i].GetValue(this));
            sb.Append(";");
        }
        return sb.ToString();
    }
}

The class EnumClassHelper has one method - ToList, which returns the list of underlying "enum elements" in our enum - like type.

public abstract class EnumClassHelper<T1, T2> : EnumValueHelper<T2>
    where T1 : class
    where T2 : class
{
    public static List<T2> ToList()
    {
        var fields = (typeof(T1)).GetFields(BindingFlags.Static | BindingFlags.Public);
        return fields.Select(x => (T2)x.GetValue(null)).ToList();
    }
}

Now we can create underlying "enum value" class of our "enum" type. Note, what the class has private constructor. It makes impossible the class object creation with the help of the C# new operator. To use this class entirely correctly, we should override some methods in the underlying enum value class of our suggested enum type:

public class EnumLikeClassValue : EnumValueHelper<EnumLikeClassValue>
{
    // Enum class data. For example one int, one double and one string
    // Only instance members!!!
    public readonly string StringValue;
    public readonly int IntValue;
    public readonly double DoubleValue;

    // parametrless ctor
    private EnumLikeClassValue()
    {
    }

    // overrides to make the stuff like operator(s) ==; etc... work correctly
    public override bool Equals(object obj)
    {
        var x = obj as EnumLikeClassValue;
        if (x == null) return false;
        return StringValue == x.StringValue && IntValue == x.IntValue && DoubleValue.Equals(x.DoubleValue);
    }

    public static bool operator ==(EnumLikeClassValue a, EnumLikeClassValue b)
    {
        // If both are null, or both are same instance, return true.
        if (System.Object.ReferenceEquals(a, b))
        {
            return true;
        }

        // If one is null, but not both, return false.
        if (((object)a == null) || ((object)b == null))
        {
            return false;
        }

        // Return true if the fields match:
        return a.Equals(b);
    }
    public static bool operator !=(EnumLikeClassValue a, EnumLikeClassValue b)
    {
        return !(a == b);
    }
    public override int GetHashCode()
    {
        return new {StringValue, IntValue, DoubleValue}.GetHashCode();
    }
}

As we can see, the methods Equals, and GetHashCode operators == and != in the underlying "enum" class have been overriden. Please remember to have the constructor in this class private

After that, we can complete our "enum" example, i.e., create our "enum" class. Please note, the members of this class should be static, so these members can be used by the ToList() function of EnumClassHelper:

public class ClassLikeEnumTest : EnumClassHelper<ClassLikeEnumTest, EnumLikeClassValue>
{
    public static readonly EnumLikeClassValue Value1 = CreateObject("StringValue1", 1, 1.0);
    public static readonly EnumLikeClassValue Value2 = CreateObject("StringValue2", 2, 2.0);
    public static readonly EnumLikeClassValue Value3 = CreateObject("StringValue3", 3, 3.0);
}

Note: As the EnumLikeClassValue class has private constructor, we cannot create EnumLikeClassValue object with new operator. We can initialize it using C# reflection - or, in our case, with the help of our ClassLikeEnumTest values: ClassLikeEnumTest.Value1, ClassLikeEnumTest.Value2, ClassLikeEnumTest.Value3.

Well, how to use it? Please look at the below sample:

class Program
{
    static void Main(string[] args)
    {
        EnumLikeClassValue value = ClassLikeEnumTest.Value1;
        Console.WriteLine(value);
        foreach (var x in ClassLikeEnumTest.ToList())
        {
            Console.WriteLine();
            Console.WriteLine(x);
            Console.WriteLine();
        }

        foreach (var x in ClassLikeEnumTest.ToList())
        {
            Console.WriteLine();
            Console.WriteLine(x == value);
            Console.WriteLine();
        }

        foreach (var x in ClassLikeEnumTest.ToList())
        {
            Console.WriteLine();
            Console.WriteLine(x.Equals(value));
            Console.WriteLine();
        }
    }
}

The output for this program is:

StringValue1;1;1;

StringValue1;1;1;

StringValue2;2;2;

StringValue3;3;3;

True

False

False

True

False

False

Press any key to continue . . .

As one can see, the source code is quite short and straightforward: it can be simply copy pasted in your project. Thanks!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Andrei Keino
Software Developer
Russian Federation Russian Federation
No Biography provided

You may also be interested in...

Comments and Discussions

 
QuestionBut what is the benefit? Pin
Prabu ram22-Jun-14 18:11
memberPrabu ram22-Jun-14 18:11 
AnswerRe: But what is the benefit? Pin
Andrei Keino22-Jun-14 19:20
memberAndrei Keino22-Jun-14 19:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.160621.1 | Last Updated 21 Jun 2014
Article Copyright 2014 by Andrei Keino
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid