The strongly typed enum pattern to get around the shortcomings of the enum type and provide a type safe, elegant and flexible way to represent enums in your applications An enum is a value type. Value types in .Net are generally stored in the stack. You typically use enums to represent named constants in your application. There are two types of enums: simple enums and flag enums. While the former type is used to represent a closed set of values, the latter is used to provide support for bitwise operations using the enum values. This article presents a discussion on enums, what they are, why they are useful, and the design constraints when using enums in applications and how to implement a type-safe enum pattern with code examples wherever appropriate. Why should we use enums? How are they helpful? Enums are helpful as you can set your domain-specific keywords that would be treated as integer constants — these all help you to write clean, readable code in your application. You can use enums in switch statements as well. You can use enums in lieu of static constants in your application. Here’s a nice article from MSDN that outlines the design guidelines that should be adhered to when working with enums: https://blogs.msdn.microsoft.com/kcwalina/2004/05/18/design-guidelines-update-enum-design/ Design and usage constraints While enums are great in helping you to write clean, readable code in your applications, they do have certain constraints as well. The .Net framework represents enums as integers. So, even if you declare an enum as having certain integer values, there is no way you can prevent your developer from assigning some other integer value to the enum you have declared. The other design constraint in using enums is that enums are not extendable. Here’s exactly where the Strongly Typed Enum design pattern comes to the rescue. The strongly typed enum pattern The strongly typed enum pattern or the type-safe enum pattern as it is called, can be used to mitigate the design and usage constraints we discussed in the earlier section when working with enums. This pattern makes sure that the type is extensible and you can use it much like an enum. Imagine that you were to build a custom logger that can log data to various configured log targets. Such log targets can be a text file, database or the event log. To design such a framework you might want to take advantage of an enum to declare the various log targets that the framework should provide support for. Now that we already know the design and usage constraints of an enum, let’s learn how we can build a type – safe representation of the same. Refer to the LogTarget class given below. public sealed class LogTarget { public static readonly LogTarget Database = new LogTarget("Database"); public static readonly LogTarget EventLog = new LogTarget("EventLog"); public static readonly LogTarget File = new LogTarget("File"); public readonly string TargetName; private LogTarget(string targetName) { TargetName = targetName; } } Note that the class has been marked as “sealed” to prevent further inheritance. The constructor is private to prevent instantiation as an instance of this class is not needed. You just have a few public readonly fields that correspond to a particular LogTarget. Assume now that you have implemented a class named CustomeLogger as shown below. Note that this is just for illustration purposes only – it is just a dummy class and is incomplete. public class CustomLogger { //Define the members of the class here public static void Log(string data, LogTarget logTarget) { //Write the necessary code here to log data to the appropriate log target } } The following code snippet shows how you can use the LogTarget class we defined earlier and call the Log method on the CustomLogger class. string data = "Data to be logged."; CustomLogger.Log(data, LogTarget.File); The caveats Albeit the fact that this design is flexible, there are certain caveats in using this pattern. You should be aware that this is nullaable (unlike an enum) and the values (the log targets that we have defined in the LogTarget class) cannot be used in “switch – case” statements. Even persistence and serialization of objects that implement the pattern we discussed in this post would need you to write some extra code. Related content feature 14 great preprocessors for developers who love to code Sometimes it seems like the rules of programming are designed to make coding a chore. Here are 14 ways preprocessors can help make software development fun again. By Peter Wayner Nov 18, 2024 10 mins Development Tools Software Development feature Designing the APIs that accidentally power businesses Well-designed APIs, even those often-neglected internal APIs, make developers more productive and businesses more agile. By Jean Yang Nov 18, 2024 6 mins APIs Software Development news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks news Go language evolving for future hardware, AI workloads The Go team is working to adapt Go to large multicore systems, the latest hardware instructions, and the needs of developers of large-scale AI systems. By Paul Krill Nov 15, 2024 3 mins Google Go Generative AI Programming Languages Resources Videos