Review of basic usage of enumeration
Here is a common oneC#
Enumeration (enum
) Example:
enum Weekday { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }
class Program { static void Main(string[] args) { Weekday today = ; ("Today is" + () + "。"); ("Tomorrow is" + (Weekday)(((int)today + 1) % 7) + "。"); ("Yesterday was" + (Weekday)(((int)today + 6) % 7) + "。"); } }
In this example, we define a name calledWeekday
enumeration, including days of each week. Then inMain
In the method, we willtoday
The variable is set toTuesday
, and useToString()
Method converts it to a string.
Next, we calculate and output tomorrow and yesterday's days. We use cast to convert the enum value to an integer and then add or subtract 1 or 6 in the meaning of modulo 7 to correctly calculate the days of the previous or next day.
The output result should be like this:
Today is Tuesday。 Tomorrow is Wednesday。 Yesterday was Monday。
Enumerate common design patterns applications
enum
It can be applied in many design modes:
- Status Mode
- Strategy Mode
- Factory model
- Observer mode
introduce
- Status Mode
State mode is used to change the behavior of an object based on its internal state.enum
It can represent the state of an object well, so it is a common choice to implement state mode. existC#
, you can useswitch
Sentences are based on differentenum
Values perform different operations.
- Strategy Mode
The policy mode allows you to select different algorithms or behaviors based on runtime conditions.enum
These conditions can be well represented, so it is a common choice for implementing policy patterns. existC#
, you can useswitch
Sentence orif-else
Sentences are based on differentenum
Value selection is different algorithm or behavior.
- Factory model
Factory mode allows you to create different objects using a common interface.enum
It can represent the types of these objects well, so it is a common choice for implementing factory patterns. existC#
, you can useswitch
Sentence orif-else
Sentences are based on differentenum
Value creates different objects.
- Observer mode
Observer pattern is used to establish loosely coupled relationships between objects.enum
It can well represent the state of the observer object, so it is a common choice to implement the observer pattern. existC#
, you can useenum
To represent the status of the observer object and use a delegate or event to notify the observer object.
Smart enumeration
What is a smart enumeration?Smart enumerationIt is not an official title, but a noun defined by the author.
This kind of enumeration with behavior can be simply defined as:Smart enumeration = enumeration + rich behavior
。
It's from the originalenum
Type (value type) has changed toclass
Types (reference type), which allows you to bind behaviors and methods to each enum type. This means you can call methods and properties on enum types just like you would call them on class instances.
Smart enumeration is just as meaningful as design patterns, helps you avoid duplicate code and improves the readability and maintainability of your code. They can also make your code more type-safe, as the compiler can verify that you are using the correct enum value.
Code Example
First, we define an abstract type for subsequent reuse:
public abstract class Enumeration<TEnum> : IEquatable<Enumeration<TEnum>> where TEnum : Enumeration<TEnum> { private static readonly Dictionary<int, TEnum?> Enumerations = GetEnumerations(); /// <summary> /// Enumeration type /// </summary> private static readonly Type EnumType = typeof(TEnum); protected Enumeration(int value, string name) { Value = value; Name = name; } /// <summary> /// Name /// </summary> public string Name { get; protected init; } /// <summary> /// Value /// </summary> public int Value { get; protected init; } public static TEnum? FromName(string name) => (x => == name); public static TEnum? FromValue(int value) => (value, out var enumeration) ? enumeration : default; public bool Equals(Enumeration<TEnum>? other) => other is not null && GetType() == () && Value == ; public override bool Equals(object? obj) => obj is Enumeration<TEnum> other && Equals(other); public override int GetHashCode() => (Value, Name); private static Dictionary<int, TEnum?> GetEnumerations() => EnumType .GetFields( | | ) .Where(x => ()) .Select(x => (TEnum?)(default)).ToDictionary(x => x?.Value ?? 0); }
Let’s briefly explain it first.
This is a generalC#
Abstract class, used to implement advanced functions of enumeration. It uses generic typesTEnum
To represent the enum type and inherit fromIEquatable<Enumeration<TEnum>>
Interface to support comparison operations.
This abstract class contains some commonly used enumeration operations, such asFromName
andFromValue
, They can get enum values by name or value. It has also been rewrittenEquals
andGetHashCode
Method so that you can compare whether the two enum values are equal.
The core method in this class isGetEnumerations
, which uses reflection to get all fields in the current enum type and converts them to enum values. In this process, it also checks if the fields are of the same type as the enum type and stores the values in a dictionary so that they can be accessed quickly later.
By inheriting this abstract class, you can easily implement your own enum type and get many useful features such as getting enum values by name and value, and supporting comparison operations.
Business Applications
We usually define the enum type as such, and use it when triggering business logic.switch
To perform different behaviors, it is easy to spread the logic in different places.
/// <summary> /// Credit card type/// </summary> public enum CreditCardType { None = 0, Standard = 1, Silver = 2, Gold = 3, }
So what should the upgraded smart enumeration look like? Where is its intelligence?
/// <summary> /// credit card/// </summary> public abstract class CreditCard : Enumeration<CreditCard> { public static readonly CreditCard Gold = new GoldCreditCard(); public static readonly CreditCard Silver = new SilverCreditCard(); public static readonly CreditCard Standard = new StandardCreditCard(); public static readonly CreditCard None = new NoneCreditCard(); private CreditCard(int value, string name) : base(value, name) { } /// <summary> /// Discount /// </summary> public abstract double Discount { get; } /// <summary> /// Gold Card /// </summary> private sealed class GoldCreditCard : CreditCard { public GoldCreditCard() : base(3, nameof(Gold)) { } public override double Discount => 0.2; } private sealed class SilverCreditCard : CreditCard { public SilverCreditCard() : base(2, nameof(Silver)) { } public override double Discount => 0.1; } /// <summary> /// standard /// </summary> private sealed class StandardCreditCard : CreditCard { public StandardCreditCard() : base(1, nameof(Standard)) { } public override double Discount => 0.05; } /// <summary> /// none /// </summary> private sealed class NoneCreditCard : CreditCard { public NoneCreditCard() : base(0, nameof(None)) { } public override double Discount => 0; } }
Let’s briefly explain it here.
This is an implementation of the credit card enumeration type, which inherits the general enumeration class mentioned earlier.Enumeration
. in,GoldCreditCard
、SilverCreditCard
、StandardCreditCard
andNoneCreditCard
There are four specific credit card subcategories.
Each subclass rewrites the parent classCreditCard
InDiscount
Attributes to indicate the discount rate of different credit cards.GoldCreditCard
There is the highest discount rate,NoneCreditCard
No discount.
existCreditCard
In the category,Gold
、Silver
、Standard
andNone
are four static instances representing four different credit card types. Each instance is created through the corresponding subclass and passed in the corresponding value and name. Values are used to identify the uniqueness of an enum type, while names are string representations of that type.
In this way, we can easily define and use different types of credit cards. For example, it can be done byCome to quote
Gold
A instance of a credit card and get its discount rate. You can also use it directly where you need to use a credit card type.CreditCard
Type to represent.
We can also use this way, throughFromName
andFromValue
:
public class SmartEnumsTests { private readonly ITestOutputHelper _testOutputHelper; public SmartEnumsTests(ITestOutputHelper testOutputHelper) { _testOutputHelper = testOutputHelper; } /// <summary> /// Basic usage /// </summary> [Fact] public void Use() { var standardCard = ("Standard"); var standardCard2 = (1); (standardCard, standardCard2); _testOutputHelper.WriteLine(standardCard!.ToJson()); } }
After reading the above sample code,Smart enumerationThe most obviousbenefitIt should be very intuitive:That is, the number of lines of code has increased by billions of points, not a little bit!
summary
Okay, it won’t go too far. Today we will briefly summarize the content.
Smart enumeration = enumeration + rich behavior
。
The above example content introduces a useC#
An example of an enumeration type implementing a credit card type. To better implement this function, we created a general enumeration class.Enumeration
, and on this basis, it has been implementedCreditCard
Class and its four specific subclasses represent different types of credit cards.
Each subclass contains an abstractDiscount
Attribute, indicating the discount rate of this type of credit card. andCreditCard
Static instances in the class represent four different credit card types. In this way, we can easily define and use different types of credit cards and use them directly where we need to use the credit card type.CreditCard
Type to represent.
This is the end of this article about how to add behavior to C# enumeration. For more related C# enumerations, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!