Introduction
This article briefly discusses some points of interest when using reflection on Enum
s. It includes some general thoughts as to the joint function of Enum
s 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 Enum
s 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
:
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):
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:
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:
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
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
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
:
Type targetClassType = targetAssembly.GetType(className);
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.
object desiredPropertyValue = Enum.Parse(enumType, enumProperyValue);
targetPropertyInfo.SetValue(targetClassInstance, desiredPropertyValue, null);
The Enum
class also has a GetNames()
function that retrieves an array of string
s containing the string
s 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:
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, enum
s, 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 Enum
s 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