在 C# 中扩展枚举

问题描述 投票:0回答:6

在java中,我习惯于扩展枚举值或重写这样的方法:

    enum SomeEnum
    {

        option1("sv")
        {
            public String toString()
            {
                return "Some value";
            }     
        }, 
        option2;

        private String PassedValue;

        public SomeEnum(String somevalue)
        {
            this.PassedValue = somevalue;
        }

        public SomeEnum()
        {
                this.PassedValue = "Default Value";
        }

        public String getPassedValue()
        {
            return this.PassedValue;
        }

    }

有没有办法在 C# 中做类似的事情,或者枚举在 C# 中受到更多限制

c# enums
6个回答
42
投票

我希望枚举在.Net 中更加强大。我喜欢.Net!您可以使用属性来完成同样的事情。下面的代码只需编写一次即可在任何地方使用。这将是一个很长的答案,但我认为这是一个非常好的解决方案,所以要有耐心!

使用方法

SomeEnum e = SomeEnum.ValueTwo;
string description = e.GetDescription();

枚举

使用属性来描述枚举及其值。

[DescriptiveEnumEnforcement(DescriptiveEnumEnforcement.EnforcementTypeEnum.ThrowException)]
public enum SomeEnum
{
    [Description("Value One")]
    ValueOne,

    [Description("Value Two")]
    ValueTwo,

    [Description("Value 3")]
    ValueThree
}

描述属性

/// <summary>Indicates that an enum value has a description.</summary>
[AttributeUsage(AttributeTargets.Field)]
public class DescriptionAttribute : System.Attribute
{
    /// <summary>The description for the enum value.</summary>
    public string Description { get; set; }

    /// <summary>Constructs a new DescriptionAttribute.</summary>
    public DescriptionAttribute() { }

    /// <summary>Constructs a new DescriptionAttribute.</summary>
    /// <param name="description">The initial value of the Description property.</param>
    public DescriptionAttribute(string description)
    {
        this.Description = description;
    }

    /// <summary>Returns the Description property.</summary>
    /// <returns>The Description property.</returns>
    public override string ToString()
    {
        return this.Description;
    }
}

描述性枚举执行属性

确保正确配置枚举的属性。

/// <summary>Indicates whether or not an enum must have a NameAttribute and a DescriptionAttribute.</summary>
[AttributeUsage(AttributeTargets.Enum)]
public class DescriptiveEnumEnforcementAttribute : System.Attribute
{
    /// <summary>Defines the different types of enforcement for DescriptiveEnums.</summary>
    public enum EnforcementTypeEnum
    {
        /// <summary>Indicates that the enum must have a NameAttribute and a DescriptionAttribute.</summary>
        ThrowException,

        /// <summary>Indicates that the enum does not have a NameAttribute and a DescriptionAttribute, the value will be used instead.</summary>
        DefaultToValue
    }

    /// <summary>The enforcement type for this DescriptiveEnumEnforcementAttribute.</summary>
    public EnforcementTypeEnum EnforcementType { get; set; }

    /// <summary>Constructs a new DescriptiveEnumEnforcementAttribute.</summary>
    public DescriptiveEnumEnforcementAttribute()
    {
        this.EnforcementType = EnforcementTypeEnum.DefaultToValue;
    }

    /// <summary>Constructs a new DescriptiveEnumEnforcementAttribute.</summary>
    /// <param name="enforcementType">The initial value of the EnforcementType property.</param>
    public DescriptiveEnumEnforcementAttribute(EnforcementTypeEnum enforcementType)
    {
        this.EnforcementType = enforcementType;
    }
}

获取描述

/// <summary>Provides functionality to enhance enumerations.</summary>
public static partial class EnumUtil
{
    /// <summary>Returns the description of the specified enum.</summary>
    /// <param name="value">The value of the enum for which to return the description.</param>
    /// <returns>A description of the enum, or the enum name if no description exists.</returns>
    public static string GetDescription(this Enum value)
    {
        return GetEnumDescription(value);
    }

    /// <summary>Returns the description of the specified enum.</summary>
    /// <param name="value">The value of the enum for which to return the description.</param>
    /// <returns>A description of the enum, or the enum name if no description exists.</returns>
    public static string GetDescription<T>(object value)
    {
        return GetEnumDescription(value);
    }

    /// <summary>Returns the description of the specified enum.</summary>
    /// <param name="value">The value of the enum for which to return the description.</param>
    /// <returns>A description of the enum, or the enum name if no description exists.</returns>
    public static string GetEnumDescription(object value)
    {
        if (value == null)
        return null;

        Type type = value.GetType();

        //Make sure the object is an enum.
        if (!type.IsEnum)
            throw new ApplicationException("Value parameter must be an enum.");

        FieldInfo fieldInfo = type.GetField(value.ToString());
        object[] descriptionAttributes = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);

        //If no DescriptionAttribute exists for this enum value, check the DescriptiveEnumEnforcementAttribute and decide how to proceed.
        if (descriptionAttributes == null || descriptionAttributes.Length == 0)
        {
            object[] enforcementAttributes = fieldInfo.GetCustomAttributes(typeof(DescriptiveEnumEnforcementAttribute), false);

            //If a DescriptiveEnumEnforcementAttribute exists, either throw an exception or return the name of the enum instead.
            if (enforcementAttributes != null && enforcementAttributes.Length == 1)
            {
                DescriptiveEnumEnforcementAttribute enforcementAttribute = (DescriptiveEnumEnforcementAttribute)enforcementAttributes[0];

                if (enforcementAttribute.EnforcementType == DescriptiveEnumEnforcementAttribute.EnforcementTypeEnum.ThrowException)
                    throw new ApplicationException("No Description attributes exist in enforced enum of type '" + type.Name + "', value '" + value.ToString() + "'.");

                return GetEnumName(value);
            }
            else //Just return the name of the enum.
                return GetEnumName(value);
        }
        else if (descriptionAttributes.Length > 1)
            throw new ApplicationException("Too many Description attributes exist in enum of type '" + type.Name + "', value '" + value.ToString() + "'.");

        //Return the value of the DescriptionAttribute.
        return descriptionAttributes[0].ToString();
    }
}

27
投票

C# 中的枚举仅适用于(整数)值;它们不能像 Java 那样有特殊的方法或构造函数。

但是,您可以定义适用于枚举的扩展方法以实现几乎相同的效果:

public enum MyEnum {
    Foo = 1,
    Bar = 2,
    Default = Foo
}

public static class MyEnumExtensions
{
    public static Widget ToWidget(this MyEnum enumValue) {
        switch (enumValue) {
        case MyEnum.Foo:
            return new Widget("Foo!");

        case MyEnum.Bar:
            return new Widget("Bar...");

        default:
            return null;
        }
    }
}

那么你可以说:

var val = MyEnum.Foo;
var widget = val.ToWidget();

3
投票
C# 中的枚举基本上只是命名的原语。它们通常基于

int

,但也可以基于任何数字基元。因此,C# 几乎不提供 Java 枚举所提供的功能。代价是 C# 枚举要轻得多,而 Java 枚举是成熟的对象。

public enum FooBar : int { Foo = 1, Bar = 2 }

上面的枚举与

int

 没有太大区别,只是我们现在可以使用 
FooBar.Foo
 代替文字 
1
。您可以在整数之间来回转换枚举,并且 
Enum
 有一些辅助函数可以在使用枚举时提供帮助。但对于 C# 来说就是这样,它们很像 C 或 C++ 枚举。


2
投票
您可以使用扩展方法在 C# 中执行类似的操作。

enum Test { Value1, Value2, Value3 } static class TextExtensions { public static string Value(this Test value) { string stringValue = default(String); switch (value) { case Test.Value1: { stringValue = "some value 1"; } break; case Test.Value2: { stringValue = "some value 2"; }; break; case Test.Value3: { stringValue = "some value 3"; }; break; } return stringValue; } } class Program { static void Main(string[] args) { Console.Write(Test.Value1.Value()); Console.ReadLine(); } }
    

1
投票
下面是我关于如何包装枚举来为您的业务案例实现自定义功能的实现。它需要一些样板文件,但使您能够使用自定义类创建自己的验证和功能,同时仍然获得标志枚举的所有

好处。 代码来自

.Net Fiddle

using System; public class MyClass { /* Flag Enum - Access via FilePerms */ [Flags] enum Flag_FilePerms : int { None = 0, Create = 1, Read = 2, Update = 4, Delete = 8 } /* FlagEnum base class */ abstract class FlagEnum { protected string __alias__; protected Type __etype__; protected Type __utype__; public FlagEnum(string sAlias, Type etype) { if (!etype.IsEnum) throw new Exception($"etype is not an Enum Type. Got: {etype.Name}"); this.__alias__ = sAlias; this.__etype__ = etype; this.__utype__ = Enum.GetUnderlyingType(etype); // Do validation here. // Eg. ensuring that sequence follows power of 2 with None = 0 as first entry ... // Note: .net does not validate this automatically with the Flags() } // implement custom methods... } /* Enum wrapper */ class Flag_FilePerms_Wrapper : FlagEnum { public Flag_FilePerms None = Flag_FilePerms.None; public Flag_FilePerms Create = Flag_FilePerms.Create; public Flag_FilePerms Read = Flag_FilePerms.Read; public Flag_FilePerms Update = Flag_FilePerms.Update; public Flag_FilePerms Delete = Flag_FilePerms.Delete; public Flag_FilePerms_Wrapper(string sAlias) : base(sAlias, typeof(Flag_FilePerms)) { } } private static void pc(object o){Console.Write($"{o}\n");} /* singleton for use in business logic */ static Flag_FilePerms_Wrapper FilePerms = new Flag_FilePerms_Wrapper("FilePerms"); static void Main(string[] args) { /* business logic */ var perms = FilePerms.Update | FilePerms.Create; pc($"perms.HasFlag(FilePerms.Create): {perms.HasFlag(FilePerms.Create)}"); /* True */ pc($"perms.HasFlag(FilePerms.Delete): {perms.HasFlag(FilePerms.Delete)}"); /* False */ var perms_none = FilePerms.None; pc($"perms_none == FilePerms.None: {perms_none == FilePerms.None}"); /* True */ pc($"perms == FilePerms.None: {perms == FilePerms.None}"); /* False */ } }

您可以实现不同的其他枚举类并实现特定于每个用例的功能。

例如:

FlagEnum:
  • 添加自定义验证以确保无输入和两个增量的幂
    • 添加特定于标志处理的功能
    IntEnum:
  • 限制多项选择
    • 值不可知(与 FlagEnum 不同)
    • 用例:错误代码等..
    StrEnum:
  • 其中 Enum 只有名称而没有指定值
    • 为将字符串处理为唯一有效的解析参数而编写的方法
    • 开箱即用的 Enum.parse 可以处理字符串和数值,并且数值可能对您的业务案例来说不明确。
      用例:工作流程状态

0
投票

public struct ExtendedHttpStatusCode { HttpStatusCode statusCode; public static ExtendedHttpStatusCode Processing = new ExtendedHttpStatusCode() { statusCode = (HttpStatusCode)102 }; public static ExtendedHttpStatusCode MultiStatus = new ExtendedHttpStatusCode() { statusCode = (HttpStatusCode)207 }; public static ExtendedHttpStatusCode UnprocessableEntity = new ExtendedHttpStatusCode() { statusCode = (HttpStatusCode)422 }; public static ExtendedHttpStatusCode Locked = new ExtendedHttpStatusCode() { statusCode = (HttpStatusCode)423 }; public static ExtendedHttpStatusCode FailedDependency = new ExtendedHttpStatusCode() { statusCode = (HttpStatusCode)424 }; public static ExtendedHttpStatusCode InsufficientStorage = new ExtendedHttpStatusCode() { statusCode = (HttpStatusCode)507 }; public static implicit operator ExtendedHttpStatusCode(HttpStatusCode value) { return new ExtendedHttpStatusCode() { statusCode = value }; } public static implicit operator HttpStatusCode (ExtendedHttpStatusCode value) { return value.statusCode; } }

然后我可以:

HttpStatusCode statusCode = ExtendedHttpStatusCode.MultiStatus; if (statusCode != HttpStatusCode.OK) // blah blah blah

一切正常。为什么?因为 C# 中的 Enum 可以包含任何值(Enum 的基础基元类型),而不仅仅是 Enum 的命名值。因此,当我们定义静态 ExtendedHttpStatusCode 属性(如 MultiStatus)时,我们只需将适当的原始值 (207) 转换为 HttpStatusCode 并将其分配给结构的相应字段。然后选角操作员会处理其他所有事情。

© www.soinside.com 2019 - 2024. All rights reserved.