{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Enums and Constants\n", "\n", "Enumerations, or 'enums' as they are commonly known, are a powerful feature in C# that allow you to assign names to integral values, making your code more readable and maintainable. They are especially useful when you want to represent a set of named constants in your program.\n", "\n", "An **enum** is a type that consists of a set of named constants that are associated with integral (numeric) values. By defining and using enum types we can make our code more human readable and constrain the potential values that can be assigned to a variable. Let's start with defining an enum type of the various suits in a standard deck of playing cards:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "public enum Suit\n", "{\n", "\t\tClubs,\n", "\t\tDiamonds,\n", "\t\tHearts,\n", "\t\tSpades\n", "}\n", "\n", "display((int)(Suit.Clubs));" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The enum is defined with a scope, public in this case, the keyword enum and a name for the type. This is followed by a collection of the enumerable values inside of a pair of curly braces `{ }` with their values and optionally a value to assign. When values are omitted, the assigned values start with zero and count upwards.\n", "\n", "We can declare a variable to be of an enum type, and assign a value with this clear syntax:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "Suit yourCardsSuit = Suit.Clubs;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The underlying value of the `myCardsSuit` variable is a `0` but we can easily read what the value is. The following code is invalid and will result in an error:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "Suit yourCardsSuit = Suit.Hammers;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is no `Hammers` value in the `Suit` enum, so this statement is invalid.\n", "\n", "### Flags\n", "Sometime we would like to store 2 or more possible values using an enum type. Traditionally, we would do this with binary values (1,2,4,8,16...) and summing those values to represent a total numeric value. With enums, we can activate a similar capability that allows a single enum variable to represent multiple values.\n", "Let's look at scheduling a recurring game night on several nights of the week. We can define as a ScheduleDayOfWeek enum like this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "[Flags]\n", "public enum ScheduleDayOfWeek\n", "{\n", "\tSunday = 1,\n", "\tMonday = 2,\n", "\tTuesday = 4,\n", "\tWednesday = 8,\n", "\tThursday = 16,\n", "\tFriday = 32,\n", "\tSaturday = 64\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These initial values correspond to the powers of 2 and with the `[Flags]` attribute decorator above the type we can use these values together in our code. Perhaps we'd like to schedule game night on Friday and Saturday nights. We can add these values together using the `|` OR operator like in this code:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var gameNights = ScheduleDayOfWeek.Friday | ScheduleDayOfWeek.Saturday;\n", "display(gameNights);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we have 1 variable that represents both nights. We can redefine the type with extra values that are a convenience and show the combination of values:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "[Flags]\n", "public enum ScheduleDayOfWeek\n", "{\n", "\tSunday = 1,\n", "\tMonday = 2,\n", "\tTuesday = 4,\n", "\tWednesday = 8,\n", "\tThursday = 16,\n", "\tFriday = 32,\n", "\tSaturday = 64,\n", "\tWeekends = 65,\n", "\tWeekdays = 62,\n", "\tEveryday = 127\n", "}\n", "\n", "var workWeek = ScheduleDayOfWeek.Weekends;\n", "display(workWeek);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`ScheduleDayOfWeek` is a Flags decorated enum, so we can use the commas to separate values and the sum of values to represent a set of values. This type of combination doesn't work with the `Suit` enum we defined earlier.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var suitsInHand = Suit.Clubs | Suit.Diamonds | Suit.Hearts;\n", "display(suitsInHand);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's... not the value we expected because it is the sum of the integral values underlying the three values combined.\n", "\n", "We can also convert from string values to the enum value using the `Parse` method. This allows us to read either a text or numeric value and convert it to the expected enum values. Consider these statements:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "display(Enum.Parse(typeof(ScheduleDayOfWeek), \"Monday\"));\n", "display(Enum.Parse(\"Tuesday\"));\n", "display(Enum.Parse(\"8\"));\n", "display(Enum.Parse(\"Thursday,Friday\"));\n", "display(Enum.Parse(\"65\"));\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we can also use the logical AND operator `&` to check if a value is part of the assigned value.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "display(ScheduleDayOfWeek.Weekends & ScheduleDayOfWeek.Friday);\n", "display(ScheduleDayOfWeek.Weekdays & ScheduleDayOfWeek.Friday);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Constants\n", " Let's also touch briefly on the topic of constants. A constant is a variable that is declared at coding time but can never be changed. This value is constant and is a good way to remove the practice of \"magic values\" in code where you might be testing or comparing to a fixed value.\n", "\n", "A constant is declared similar to a normal variable, but with the addition of the `const` keyword. I could have code that looks like the following:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "if (DateTime.Now.Hour <= 6)\n", "{\n", "  display(\"You should still be sleeping, it's before 6!\");\n", "}\n", "else\n", "{\n", "  display(\"You should be awake, it's after 6\");\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That 6 in the test appears in the descriptions, and if my wake up hour changes, all of that code and the messages need to be re-written. By introducing a constant we can make this easier to read and re-use the value:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "const int WakeUpHour = 6;\n", "if (DateTime.Now.Hour <= WakeUpHour)\n", "{\n", "\tdisplay($\"You should still be sleeping, it's before {WakeUpHour}!\");\n", "}\n", "else\n", "{\n", "\tdisplay($\"You should be awake, it's after {WakeUpHour}\");\n", "}\n" ] } ], "metadata": { "kernelspec": { "display_name": ".NET (C#)", "language": "C#", "name": ".net-csharp" }, "language_info": { "name": "polyglot-notebook" }, "polyglot_notebook": { "kernelInfo": { "defaultKernelName": "csharp", "items": [ { "aliases": [], "languageName": "csharp", "name": "csharp" } ] } } }, "nbformat": 4, "nbformat_minor": 2 }