Click here to Skip to main content
15,880,956 members
Articles / Programming Languages / C#
Article

Enum Reflection

Rate me:
Please Sign up or sign in to vote.
3.06/5 (14 votes)
22 Nov 2008CPOL8 min read 59.5K   308   13   7
An article about using reflection with Enums

Introduction

This article briefly discusses some points of interest when using reflection on Enums. It includes some general thoughts as to the joint function of Enums and reflection and also some code to demonstrate a few delicate points.

Background

I have been using reflection for some refined duties in a project I am involved in. I found the reflection on Enums to be a bit tricky and after learning the secrets, I decided to share them in (I hope) an orderly fashion.

Reflecting on Enums

Scenario

Suppose you have an XML file that tells you to instantiate a certain class, of a certain assembly, and assign a certain value to one of its properties which is, of course, of an Enum type. All data is string (you would probably not get that bad but this example is not that unrealistic).

Example

We will use a short code example (get it at the top of the page) to demonstrate the point (and to hint at the time this article has been written).

The Type Library

Assembly EnumReflectionDll contains a class named Person which has one property called VotedFor:

C#
namespace EnumReflectionDll
{
    public class Person
    {
        private Candidates _votedFor;

        public Candidates VotedFor
        {
            get { return _votedFor; }
            set { _votedFor = value; }
        }
    }
}

The property is of the Enum type Candidates (also defined in this Assembly):

C#
namespace EnumReflectionDll
{
    public enum Candidates
    {
        None=0,
        Obama,
        MacCaine,
    }
}

Recall that an Enum is a value type (think of int for an example). This means a variable of type Candidates cannot be null therefore, it must have a default value, the same way int has a default of 0 which is definitely a specific value and not null. Think of a string which is not a reference type, although behaving like it in some ways, and has an initial value of null but can be assigned the empty string (""). Since I don't want to be accused of political bias, I have assigned the value 0 to the None option which makes it the default.

The Client Program

The program using this assembly, EnumReflectionDemo, has no idea about this DLL. So much so that it doesn't even reference it. It only has one static method called from the Program.Main() method:

C#
static void Main(string[] args)
{
    object something = CreateObjectWithEnum(@"c:\EnumReflectionDll.dll", 
			"EnumReflectionDll.Person", "VotedFor", "MacCaine");
}

This method takes all four parameters (assembly location, name of type to be instantiated, the name of a property to be set, and the value to assign it) and returns the required object properly set.

Build Events

Before examining the reflection, note that the assembly EnumReflectionDll.dll is loaded by the code from path c:\EnumReflectionDll.dll but how does it get to c:\. You could build it, copy it from the bin\Release or bin\Debug directory to c:\ but every time you change and rebuild the assembly, you would have to copy the fresh DLL to c:\ (which ends with you wondering how come you hanged the code and built it and nothing changes).

Wouldn't it be nice if someone copied the *.dll to c:\ for you every time you built it? Well, there is someone. Click the project properties. Note the "Build Events" page. Here you can define pre-build events and post build events:

Image 1

The syntax is much like DOS syntax (that is if you are as old as I am) and you can look it up over the Internet (if you are younger).

The Code

Getting the Assembly

C#
// Load target assembly:
Assembly targetAssembly = Assembly.LoadFile(assemblyPath);

Here we get that assembly by dynamically loading it from a file. If you have a system made of a few assemblies you use by reference, check out Assembly.GetAssembly(Type). This function gets the assembly where a given type is defined. This sometimes makes your code more readable and can sometimes save the effort of trying to figure out which assembly is calling me, etc. Of course, if the assembly is the running assembly use Assembly.GetRunningAssembly().

Creating an Instance of the Object

C#
// Create an instance of the needed object:
object targetClassInstance = targetAssembly.CreateInstance(className);

Not much to say here.

Getting the Property Type

Note that our function does not receive the type of the target property (the enum type Candidates) as a parameter. We would have to reflect on the object type, get the property info and get the type form PropertyInfo.PropertyType:

C#
// Get the type of the needed object:
Type targetClassType = targetAssembly.GetType(className);
// Get the property info for the target property:
PropertyInfo targetPropertyInfo = targetClassType.GetProperty(enumProperyName);
Type enumType = targetPropertyInfo.PropertyType;

Assigning a Value to the Property

This is the tricky part. I first expected to cerate an instance of the Enum type and set it to the property using PropertyInfo.SetValue() but the property is of an Enum type, not a class. So what does "create an instance" mean here?

Note that for other value types (int, float, bool, etc.) you just assign the value "as is" but what do we do with an Enum? How do we transform the string representing the enum value to a real "Enum value". Well, at first I expected to find this in the System.Reflection namespace but after failing miserably, I found it in the Enum class (there is one). Link other value types, the Enum class has a Parse() method that takes a string and returns that elusive "value of an Enum type". Note that this static method also takes the type of the Enum from which we are getting the value and where this string is a valid value.

C#
// Get the desired value for the property:
object desiredPropertyValue = Enum.Parse(enumType, enumProperyValue);
// Assign the value to the property:
targetPropertyInfo.SetValue(targetClassInstance, desiredPropertyValue, null);

The Enum class also has a GetNames() function that retrieves an array of strings containing the strings defined for a given Enum.

Running the code and braking after the function has returned, we can validate that we indeed have an object of type person and this person is indeed a republican:

Image 2

God bless America!

Some Thoughts about Reflection, Generics, Type Safety, XML and Enums

(You MUST be wondering how I got all these together…)

Type Safety

If you have been writing code long enough and maybe in a few languages, you are probably familiar with "type loose" bugs. "Type loose" bugs are bugs that could have easily been prevented just by knowing what exactly the variable holds. This kind of bug is most difficult to find because it can show up unpredictably at any time. If you have no idea what I am talking about, try imagining programming when all your variables are of type object. Type safe languages such as the C family do a great deal of help by preventing type loose bugs as early as possible. Visual Studio (especially so if you have ReSharper installed) does a great job in telling you exactly what you are doing wrong in terms of type. You could extensively use object but, why would you?

Generics

Type safety comes with a price. If something is of a certain type then it is of that type and therefore cannot be anything else. The problem here jumps up when you think about collections. If you build a collection for, say, tables you cannot use it for chairs. That is why . .NET 1 collections were all collections of objects. You can put in whatever you want and you even can mix your object types. And there goes type safety for collections. That is why we were introduced with generics in .NET 2 (If you are not familiar with generics, please do get familiarized).

XML

The thing is, that if you want to be type safe, you must be creative. You have solved the collections problem with generics but other problems do and will come up. Problems that would make the programmer resort to the all consuming object type. An interesting example for this is XML. XML is a way of describing data textually. This data can (sometimes) be read by humans, it can be read by different systems (operating systems, languages, etc.), it can change and you can reload it without rebuilding your code and it has some other cool features. (If you are not familiar with XML please do get familiarized.) Microsoft has adopted XML (WPF XAML is a version of XML) and it is also be used as the base of popular communications standards.

So, if you want to be type safe and still be able to describe some of your objects in text outside of your code, you need reflection.

Reflection

Reflection is the winning of the "cool .NET feature" award since the framework was invented (still takes WPF…). Described shortly – reflection is your tool for accessing all data about the types in an assembly (all classes, interface, enums, etc. in a DLL or EXE). With reflection, you can do really cool stuff like create instances of a type on the fly, call methods and events, dynamically create types. (If you are not familiar with reflection, please do get familiarized.) With reflection, you can freely write XML (or other text for that matter) and have it "translated" into the type safe language of your code.

Reflection would be the format "non type safe" world where you can do the grey stuff if you have to. By isolating it effectively and in an orderly fashion, you keep your type safe code - tightly type safe.

Enums

Enum are, after all, numeric variables. The advantage of using an Enum is in the spirit of type safety: you can assign the variable only a meaningful number (this is not completely true), and you also get to see in your code that the value 2 is in fact representing Beverages.Cola. Since Enums are a bit tricky in reflections but important to the concepts just discussed, I wrote this article.

Acknowledgement

I was introduced to much of this stuff by Dov & Avraham Zilberman from Jerusalem, to whom I am very thankful.

History

  • 20th November, 2008: Initial version

License

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


Written By
Chief Technology Officer Ziv systems, Israel
Israel Israel
Starting with Apple IIe BASICA, and working my way through Pascal, Power Builder, Visual basic (and the light office VBA) C, C++, I am now a full stack developer and development manager. Mostly with MS technologies on the server side and javascript(typescript) frameworks on the client side.

Comments and Discussions

 
GeneralThanks Pin
cgrammer5-Sep-09 6:55
cgrammer5-Sep-09 6:55 
GeneralRe: Thanks Pin
Asher Barak5-Sep-09 19:33
professionalAsher Barak5-Sep-09 19:33 

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.