Location>code7788 >text

Open source - Ideal library - Enumeration extension design ideas and implementation difficulties (III)

Popularity:332 ℃/2024-11-18 23:55:32

Today I want to share with you about enumeration extension design ideas and difficulties encountered in the implementation process.

01Design Ideas

The design idea is actually very simple, that is, through the enumeration of related information: enumeration value, enumeration name, enumeration description, enumeration item, enumeration type, a variety of conversions, through a piece of information to obtain other information. For example, through the enumeration item to get enumeration description, through the enumeration type to get enumeration name-enumeration description key-value pairs for drop-down lists and so on.

The main types of conversion implementations include the following:

(1) Converted by enumeration value to: enumeration, enumeration name, enumeration description;

(2) Converted by enumeration name: enumeration, enumeration value, enumeration description;

(3) Converted by enumeration description: enumeration, enumeration value, enumeration name;

(4) Conversion by enumeration items: enumeration value, enumeration description;

(5) Converted to a set of key-value pairs by enumeration type: enumeration value-enumeration name, enumeration value-enumeration description, enumeration name-enumeration value, enumeration name-enumeration description, enumeration description-enumeration value, enumeration description-enumeration name, and so on;

02Realization Difficulties

The implementation did encounter some difficulties as well as small details that needed attention.

1. Small details in enumeration name to enumeration

Ran into a small pitfall in the implementation of enum name to enum.

The enumeration itself provides methods to convert enumeration name strings or enumeration value strings to enumerations, so we chose this method to implement enumeration name to enumeration. However, our own interface is designed to convert enumeration names to enumerations, so we need to rule out the possibility of miscommunicating enumeration value strings.

Scheme: when the conversion is successful, we also need to call the method to check if the enum name string is a valid enum name string, and if it is not an existing valid enum item in the enum, we also need to consider if it is a bit flag combination case.

The code is as follows:

//Convert to an enumeration based on the name of the enumeration, the conversion fails to return null
public static TEnum? ToEnumByName<TEnum>(this string name)
    where TEnum : struct, Enum
{
    // Return the result if the conversion is successful, otherwise return null.
    if (<TEnum>(name, out var result))
    {
        // check if it is a valid enum name string.
        if ((typeof(TEnum), name))
        {
            // Return the enumeration
            return result;
        }
        else
        } else {
            //Calculate whether the bit flag combination is a valid item or not
            var isValidFlags = IsValidFlagsMask<ulong, TEnum>(<ulong>());
            // Returns the enumeration if it is a valid bit-flag combination item, otherwise returns null
            return isValidFlags ? result : default(TEnum?);
        }
    }
    // Return null
    return default; }
}

2, enumeration description to small details in the enumeration

In the implementation of enumeration description to enumeration, special care should be taken in the handling of enumerations with bit flags. We know that a feature of bit-flagged enumerations is that enumeration items can be combined with each other by bit manipulation to get a valid enumeration item that does not exist in the currently defined enumeration. This requires us to handle the combination case well.

OPTION: The following steps can therefore be followed.

(1) Prioritize the case when the enumeration does not carry bit flags;

(2) Re-processing the case with bit flags;

Bit-flag combinations are spliced by the English comma [,], so also consider the case if a description string has the English comma [,] in it but is not itself a combination.

(3) First treat the description as if it were not a combinatorial case, process it once first, and end the conversion if it is successful;

(4) In case of combination, each enumeration value of the combination is calculated and its corresponding enumeration item is obtained by bitwise operation;

And in the case of combinations it is also important to consider that this enumeration description transformation should be marked as invalid if there is a combination of items that are invalid at the time of the combination.

(5) Determine that each item of the combination is correct, otherwise return null;

(6) Converts the enumeration term computed by combination to an enumeration;

Because through the enumeration value to enumeration process also need to specify the enumeration value type to be able to, so the last step also need to call different conversion methods according to the actual base type of the enumeration type.

The specific code is as follows:

//Convert to enumeration according to the description of the enumeration, conversion failure return null
public static TEnum? ToEnumByDesc<TEnum>(this string description)
    where TEnum : struct, Enum
Enum {
    var type = typeof(TEnum);; var info = GetEnumType
    var info = GetEnumTypeInfo(type);
    // Handling of cases that are not enumerations of bit flags
    if (!)
    {
        return ToEnumDesc<TEnum>(description);
    }
    //Handle if it is a bit flag enumeration.
    // not the case of combining bits, which may themselves contain [,].
    var tenum = ToEnumDesc<TEnum>(description);
    if ()
    return tenum; if ()
        return tenum; }
    }
    // If it doesn't contain [,], then return it directly
    if (! (','))
    {
        return default;
    }
    //It's the case of combining bits
    var names = (',');
    var values = (type);
    // Record the number of valid enumeration descriptions
    var count = 0; var values = (type); //record the number of valid enumeration descriptions
    ulong mask = 0L;
    // variable enumerate all items
    foreach (var name in names)
    {
        foreach (Enum value in values)
        {
            //Take the enumeration description and compare it to the target description, return the enumeration if it is the same.
            if (() == name)
            {
                // Add 1 to the number of valid enumerations.
                count++; //Convert the enumeration to a value.
                //Convert the enumeration value to long.
                var valueLong = Convert.ToUInt64(value); // Filter out negative or invalid values.
                // Filter out negative or invalid values, canonical bit-flagged enumerations should always be non-negative
                if (valueLong >= 0)
                {
                    // Merge the enumeration values into mask
                    mask |= valueLong; }
                }
                break; }
            }
        }
    }
    //If they are not equal, the description string is not a valid combination item
    if (count ! = )
    {
        return default; }
    }
    var underlyingType = (type); if (underlyingType == typeof(byte)) { return default; }
    if (underlyingType == typeof(byte))
    {
        return ((byte)mask).ToEnumByValue<TEnum>();
    }
    else if (underlyingType == typeof(sbyte))
    {
        return ((sbyte)mask).ToEnumByValue<TEnum>(); }
    }
    else if (underlyingType == typeof(short))
    {
        return ((short)mask).ToEnumByValue<TEnum>(); }
    }
    else if (underlyingType == typeof(short))
    {
        return ((ushort)mask).ToEnumByValue<TEnum>(); } else if (underlyingType == typeof(ushort) { return ((ushort)mask).
    }
    else if (underlyingType == typeof(int))
    {
        return ((int)mask).ToEnumByValue<TEnum>(); }
    }
    else if (underlyingType == typeof(uint))
    {
        return ((uint)mask).ToEnumByValue<TEnum>(); } else if (underlyingType == typeof(uint) {
    }
    else if (underlyingType == typeof(long))
    } else if (underlyingType == typeof(long))
        return ((long)mask).ToEnumByValue<TEnum>(); } else if (underlyingType == typeof(long) {
    }
    else if (underlyingType == typeof(ulong))
    {
        return <TEnum>(); } else if (underlyingType == typeof(ulong)) {
    }
    return default; }
}

3, enumeration to enumeration value in small details

We know that when defining an enumeration we can specify the enumeration value type to be one of the following eight types sbyte, byte, short, ushort, int, uint, long, ulong, so all the methods involved in returning an enumeration value we have to take into account the support for the different types of enumeration value types returned.

We also know that the same method name and inputs do not distinguish different overloaded methods by their different return types.

Scenario: so we can support different types of enumerated value types returned by means of generalization.

4. Small details of conversion by enumerated values

We know from the above that there are eight enumerated value types, so when it comes to converting enumerated values to other information, you need to be compatible with all eight types.

To achieve this functionality, you can use the above mentioned generic types, if we use generic types we can use struct type to restrict the enumeration value generic type TValue, because struct can cover the eight types of enumeration values, but it also raises another problem is that float, double, DateTime are struct types, which leads to these types in the ToEnumByValue and other related methods can also be pointed out in the editor.

As a public wrapper method, this approach is clearly user-unfriendly.

So we choose another way: overloading the method to implement it, by encapsulating the method with some more code to achieve user-friendly calls.

5, how to efficiently return key-value pair data

When converting enumerated types to various collections of key-value pairs, some of the methods require reflection, so how to efficiently obtain this information becomes a top priority.

The most straightforward idea is to cache the set of key-value pairs corresponding to each enumeration type, and then fetch them directly from the cache the next time they are used.

But in careful detail, there are 3 pieces of data: enumeration value, enumeration name, and enumeration description. There are 6 cases of two-by-two combinations, which means that it takes 6 caches to store 12 pieces of data, which actually translates to 3 times the amount of wasted space.

Of course we could store a copy of these three values and then just combine them to get them when needed, but let's analyze carefully whether it's necessary to cache all the data.

Enumeration values involve type conversion, enumeration names involve calling ToString methods, and enumeration descriptions require reflection. Enumeration descriptions are the best performers, so enumeration descriptions definitely need to be cached, and enumeration values and enumeration names can be considered to be cached.

If we only cache enumeration descriptions, then enumeration values and names are converted directly when used, so we perform a cache of enumeration descriptions and a cache of enumeration types corresponding to all their enumerations. In addition, the enumerations can be recorded with or without bitwise flags and masks.

The code is as follows:

public static partial class EnumExtension
{
    //Enumeration type base information
    private sealed class EnumTypeInfo
    {
        //Whether it is a bit flag
        public bool IsFlags { get; set; }
        //Enumeration Mask
        public ulong Mask { get; set; }
        //enumeration set
        public List<Enum> Items { get; set; } = [];
    }
    //Stores the description corresponding to the enumeration item
    private static readonly ConcurrentDictionary<Enum, string> _descs = new();
    //Storage Enumeration Related Information

Get enumeration name-enumeration description key-value pair code is as follows, first get the enumeration type base information, then convert the enumeration item collection to key-value pair collection.

//Get enum name + enum description
public static Dictionary<string, string> ToEnumNameDescs(this Type type)
ToEnumNameDescs(this Type)
    //Get the enumeration type base information based on type.
    var info = GetEnumTypeInfo(type);
    // Go to the target type from the set of enumeration entries
    return (r => (), r => ());
}

6. How to recognize whether an enumeration value is a valid bit flag combination

How to identify an enumerated value is a valid combination of bit flags, can be said to be the most difficult part of the code, in fact, also mentioned earlier, the main application of the idea of masking, the previous chapter has been explained in detail to explain the here will not repeat.

Later on I will upload the library to Nuget so you can use it directly.

classifier for sums of money: The test method code as well as the sample source code have been uploaded to the code repository for those who are interested./hugogoos/Ideal