Enums in C# can make you code easier to read:
private enum Status
{
Awesome = 1,
Cool = 2
};
public void Sample()
{
var lego = new Lego();
lego.Everything = Status.Awesome;
if (lego.PartOfTeam == true)
{
lego.Everything = Status.Cool;
}
}
But enums don't cross in and out of C# easily.
Have you ever tried to save an enum to SQL? It becomes an int
and you're right back to, "Wait, what does a Status of 2 mean again?".
It's easy convert the enum to a string
before sending it to SQL, but then you have to convert it back to an enum in C# when you read it in, and that code is gross:
public string ConvertEnumToString(Status status)
{
return status.ToString();
}
public Status ConvertStringToEnum(string status)
{
return (Status) Enum.Parse(typeof (Status), status);
}
What about sending the value down to the client in JSON? Again, by default you get an int
that looses the point of having the enum in the first place.
That is, unless you want to add enum converters from Json.NET to your JsonSerializer
to turn them into strings:
serializer.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
That's not so bad. Plus, what's the alternative?
The simplest alternatives are primitive types that convert across all layers, like ints
and strings
. I prefer strings
so I can immediately know what the value represents and code around that (e.g., the Status is "Cool" instead of 2).
"But this is using magic strings
, and those are bad." Maybe. But 1) everyone likes magic, and 2) what's the risk?
That you mistype the string
? There is type-safety risk, but it's a mild one and only applies to C#. Let's say you're in JavaScript and looking for that status of "Coll" instead of "Cool". That code will fail. How long until you figure that out? You'll have tests and/or run the code yourself and see it doesn't work, right?
Also, if you work with a dynamic language, you know you can type in any nonsense and your compiler won't help you. It's up to you to test it. I see magic strings
the same way. Yes, you might make a mistake the compiler doesn't catch, but that's why we test our code.
What about the risk that the value changes in the future. Let's say management decides that statuses of "Awesome" are too strong. They want you to change all occurrences of "Awesome" to "Good" throughout the app. What's the damage here?
Well if you have C# enums, you change Status.Awesome
to Status.Good
everywhere, then you update you SQL data so old records are correct (only needed if you converted the enum values to strings
before storing them), then you update your JavaScript so it doesn't look for or branch on the wrong string
(again, only needed if you serialized your C# enums to strings).
If you didn't use enums at all, and just used strings
in C#, you would have the same SQL and JavaScript updating to do, but maybe more C# strings
to find/replace throughout the project. But I don't think you can reasonably say that's a lot more work. Maybe a few minutes more?
On top of that, what's the risk that enums change in the future like this. It's not zero, but it's really low. Enum values should be immutable and usually represent the type or state of something, so enum values don't change their meanings very often.
So why go to the trouble to store C# values in a special type that SQL and JavaScript can't work with? A magic string
is just as easy to read as the enum, and it works up and down the stack.
Here's the original code, but with magic string
s instead of enums. I don't think it loses any clarity:
public void Sample()
{
var lego = new Lego();
lego.Everything = "Awesome";
if (lego.PartOfTeam == true)
{
lego.Everything = "Cool";
}
}
Concerns with this approach
This post has generated a lot of comments! It's been a good discussion on the tradeoffs of using C# enums. Some people have agreed with me that it is simpler to use magic strings
because they work across languages.
Some have worried about database space used storing strings
vs. ints
. Strings
do take up more disk space, but I'd argue we're living in the age of not worrying about that, so take advantage of it. Others have pointed out databases can sort, index, and pull ints
faster than strings
. If storage costs or milliseconds matter in your situation, you'll need to factor that in.
Others have agreed with the point that enum conversions can be messy, but think I've gone too far with magic strings
everywhere. The consensus with this crowd seems to be maybe skip enums, but at least use C# static classes. I like this, and that's what I'm doing these days:
public static class Status
{
public const string Awesome = "Awesome";
public const string Cool = "Cool";
}
lego.Everything = Status.Awesome;
Still others want their C# enums just like they are and don't mind converting in JavaScript and SQL if needed.
It's up to you to decide what makes the most sense for your project.