SoFunction
Updated on 2025-03-01

How to add behavior in C# enumeration

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 calledWeekdayenumeration, including days of each week. Then inMainIn the method, we willtodayThe 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

enumIt 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.enumIt can represent the state of an object well, so it is a common choice to implement state mode. existC#, you can useswitchSentences are based on differentenumValues ​​perform different operations.

  • Strategy Mode

The policy mode allows you to select different algorithms or behaviors based on runtime conditions.enumThese conditions can be well represented, so it is a common choice for implementing policy patterns. existC#, you can useswitchSentence orif-elseSentences are based on differentenumValue selection is different algorithm or behavior.

  • Factory model

Factory mode allows you to create different objects using a common interface.enumIt can represent the types of these objects well, so it is a common choice for implementing factory patterns. existC#, you can useswitchSentence orif-elseSentences are based on differentenumValue creates different objects.

  • Observer mode

Observer pattern is used to establish loosely coupled relationships between objects.enumIt can well represent the state of the observer object, so it is a common choice to implement the observer pattern. existC#, you can useenumTo 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 originalenumType (value type) has changed toclassTypes (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 typesTEnumTo represent the enum type and inherit fromIEquatable<Enumeration<TEnum>>Interface to support comparison operations.

This abstract class contains some commonly used enumeration operations, such asFromNameandFromValue, They can get enum values ​​by name or value. It has also been rewrittenEqualsandGetHashCodeMethod 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.switchTo perform different behaviors, it is easy to spread the logic in different places.

/// &lt;summary&gt;
/// Credit card type/// &lt;/summary&gt;
public enum CreditCardType
{
    None = 0,
    Standard = 1,
    Silver = 2,
    Gold = 3,
}

So what should the upgraded smart enumeration look like? Where is its intelligence?

/// &lt;summary&gt;
/// credit card/// &lt;/summary&gt;
public abstract class CreditCard : Enumeration&lt;CreditCard&gt;
{
    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)
    {
    }

    /// &lt;summary&gt;
    /// Discount    /// &lt;/summary&gt;
    public abstract double Discount { get; }

    /// &lt;summary&gt;
    /// Gold Card    /// &lt;/summary&gt;
    private sealed class GoldCreditCard : CreditCard
    {
        public GoldCreditCard() : base(3, nameof(Gold))
        {
        }

        public override double Discount =&gt; 0.2;
    }

    private sealed class SilverCreditCard : CreditCard
    {
        public SilverCreditCard() : base(2, nameof(Silver))
        {
        }

        public override double Discount =&gt; 0.1;
    }

    /// &lt;summary&gt;
    /// standard    /// &lt;/summary&gt;
    private sealed class StandardCreditCard : CreditCard
    {
        public StandardCreditCard() : base(1, nameof(Standard))
        {
        }

        public override double Discount =&gt; 0.05;
    }

    /// &lt;summary&gt;
    /// none    /// &lt;/summary&gt;
    private sealed class NoneCreditCard : CreditCard
    {
        public NoneCreditCard() : base(0, nameof(None))
        {
        }

        public override double Discount =&gt; 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,GoldCreditCardSilverCreditCardStandardCreditCardandNoneCreditCardThere are four specific credit card subcategories.

Each subclass rewrites the parent classCreditCardInDiscountAttributes to indicate the discount rate of different credit cards.GoldCreditCardThere is the highest discount rate,NoneCreditCardNo discount.

existCreditCardIn the category,GoldSilverStandardandNoneare 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 quoteGoldA 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.CreditCardType to represent.

We can also use this way, throughFromNameandFromValue

public class SmartEnumsTests
{
    private readonly ITestOutputHelper _testOutputHelper;

    public SmartEnumsTests(ITestOutputHelper testOutputHelper)
    {
        _testOutputHelper = testOutputHelper;
    }

    /// &lt;summary&gt;
    /// Basic usage    /// &lt;/summary&gt;
    [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 implementedCreditCardClass and its four specific subclasses represent different types of credit cards.

Each subclass contains an abstractDiscountAttribute, indicating the discount rate of this type of credit card. andCreditCardStatic 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.CreditCardType 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!