在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# 中受到更多限制
我希望枚举在.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();
}
}
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();
int
,但也可以基于任何数字基元。因此,C# 几乎不提供 Java 枚举所提供的功能。代价是 C# 枚举要轻得多,而 Java 枚举是成熟的对象。
public enum FooBar : int {
Foo = 1,
Bar = 2
}
上面的枚举与
int
没有太大区别,只是我们现在可以使用
FooBar.Foo
代替文字
1
。您可以在整数之间来回转换枚举,并且
Enum
有一些辅助函数可以在使用枚举时提供帮助。但对于 C# 来说就是这样,它们很像 C 或 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();
}
}
:
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:
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 并将其分配给结构的相应字段。然后选角操作员会处理其他所有事情。