我有以下枚举:
public enum AuthenticationMethod
{
FORMS = 1,
WINDOWSAUTHENTICATION = 2,
SINGLESIGNON = 3
}
然而问题是,当我要求 AuthenticationMethod.FORMS 而不是 id 1 时,我需要“FORMS”这个词。
我找到了解决此问题的以下方法 (link):
首先,我需要创建一个名为“StringValue”的自定义属性:
public class StringValue : System.Attribute
{
private readonly string _value;
public StringValue(string value)
{
_value = value;
}
public string Value
{
get { return _value; }
}
}
然后我可以将此属性添加到我的枚举器中:
public enum AuthenticationMethod
{
[StringValue("FORMS")]
FORMS = 1,
[StringValue("WINDOWS")]
WINDOWSAUTHENTICATION = 2,
[StringValue("SSO")]
SINGLESIGNON = 3
}
当然,我需要一些东西来检索那个 StringValue:
public static class StringEnum
{
public static string GetStringValue(Enum value)
{
string output = null;
Type type = value.GetType();
//Check first in our cached results...
//Look for our 'StringValueAttribute'
//in the field's custom attributes
FieldInfo fi = type.GetField(value.ToString());
StringValue[] attrs =
fi.GetCustomAttributes(typeof(StringValue),
false) as StringValue[];
if (attrs.Length > 0)
{
output = attrs[0].Value;
}
return output;
}
}
现在很好,我已经有了获取枚举器字符串值的工具。然后我可以像这样使用它:
string valueOfAuthenticationMethod = StringEnum.GetStringValue(AuthenticationMethod.FORMS);
好吧,现在所有这些工作都像一个魅力,但我发现它有很多工作。我想知道是否有更好的解决方案。
我还尝试了一些带有字典和静态属性的东西,但这也不是更好。
Enum
前面添加 this
即可使其成为扩展方法。然后你可以做AuthenticationMethod.Forms.GetStringValue();
尝试 type-safe-enum 模式。
public sealed class AuthenticationMethod {
private readonly String name;
private readonly int value;
public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (1, "FORMS");
public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (2, "WINDOWS");
public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (3, "SSN");
private AuthenticationMethod(int value, String name){
this.name = name;
this.value = value;
}
public override String ToString(){
return name;
}
}
更新显式(或隐式)类型转换可以通过
添加静态字段映射私有静态只读 Dictionary
nb 为了使“枚举成员”字段的初始化在调用实例构造函数时不会引发 NullReferenceException,请务必将 Dictionary 字段放在类中的“枚举成员”字段之前。这是因为静态字段初始化器是按声明顺序调用的,并且在静态构造函数之前,造成了奇怪且必要但令人困惑的情况,即可以在所有静态字段初始化之前和调用静态构造函数之前调用实例构造函数。
在实例构造函数中填充这个映射 instance[name] = this;
并添加自定义类型转换算子 public static explicit operator AuthenticationMethod(string str) { AuthenticationMethod result; if (instance.TryGetValue(str, out result)) 返回结果;否则抛出新的 InvalidCastException(); }
使用方法
Enum.GetName(Type MyEnumType, object enumvariable)
如(假设 Shipper
是一个定义的枚举)
Shipper x = Shipper.FederalExpress;
string s = Enum.GetName(typeof(Shipper), x);
Enum 类上还有许多其他静态方法也值得研究......
您可以使用 ToString() 引用名称而不是值
Console.WriteLine("Auth method: {0}", AuthenticationMethod.Forms.ToString());
文档在这里:
http://msdn.microsoft.com/en-us/library/16c1xs4z.aspx
...并且如果您以 Pascal Case 命名您的枚举(就像我所做的那样 - 例如 ThisIsMyEnumValue = 1 等),那么您可以使用一个非常简单的正则表达式来打印友好的形式:
static string ToFriendlyCase(this string EnumString)
{
return Regex.Replace(EnumString, "(?!^)([A-Z])", " $1");
}
可以很容易地从任何字符串调用:
Console.WriteLine("ConvertMyCrazyPascalCaseSentenceToFriendlyCase".ToFriendlyCase());
输出:
将我的疯狂帕斯卡案例句子转换为友好案例
这样可以节省在房子周围跑来跑去创建自定义属性并将它们附加到您的枚举或使用查找表将枚举值与友好字符串结合起来,最重要的是它是自我管理的,并且可以用于任何无限的 Pascal Case 字符串更可重复使用。当然,它不允许您使用与您的解决方案提供的枚举不同的友好名称。
不过,对于更复杂的场景,我确实喜欢您的原始解决方案。您可以将您的解决方案更进一步,让您的 GetStringValue 成为您的枚举的扩展方法,然后您就不需要像 StringEnum.GetStringValue 那样引用它...
public static string GetStringValue(this AuthenticationMethod value)
{
string output = null;
Type type = value.GetType();
FieldInfo fi = type.GetField(value.ToString());
StringValue[] attrs = fi.GetCustomAttributes(typeof(StringValue), false) as StringValue[];
if (attrs.Length > 0)
output = attrs[0].Value;
return output;
}
然后,您可以直接从您的枚举实例轻松访问它:
Console.WriteLine(AuthenticationMethod.SSO.GetStringValue());
"(?!^)([^A-Z])([A-Z])", "$1 $2"
。所以 HereIsATEST
变成 Here Is ATEST
。
不幸的是,在枚举上获取属性的反射非常慢:
看到这个问题:Anyone know a quick way to get to custom attributes on an enum value?
.ToString()
在枚举上也很慢。
您可以为枚举编写扩展方法:
public static string GetName( this MyEnum input ) {
switch ( input ) {
case MyEnum.WINDOWSAUTHENTICATION:
return "Windows";
//and so on
}
}
这不是很好,但会很快并且不需要属性或字段名称的反射。
C#6 更新
如果您可以使用 C#6,那么新的 nameof
运算符适用于枚举,因此 nameof(MyEnum.WINDOWSAUTHENTICATION)
将在编译时转换为 "WINDOWSAUTHENTICATION"
,使其成为获取枚举名称的最快方法。
请注意,这会将显式枚举转换为内联常量,因此它不适用于变量中的枚举。所以:
nameof(AuthenticationMethod.FORMS) == "FORMS"
但...
var myMethod = AuthenticationMethod.FORMS;
nameof(myMethod) == "myMethod"
我使用扩展方法:
public static class AttributesHelperExtension
{
public static string ToDescription(this Enum value)
{
var da = (DescriptionAttribute[])(value.GetType().GetField(value.ToString())).GetCustomAttributes(typeof(DescriptionAttribute), false);
return da.Length > 0 ? da[0].Description : value.ToString();
}
}
现在用以下内容装饰 enum
:
public enum AuthenticationMethod
{
[Description("FORMS")]
FORMS = 1,
[Description("WINDOWSAUTHENTICATION")]
WINDOWSAUTHENTICATION = 2,
[Description("SINGLESIGNON ")]
SINGLESIGNON = 3
}
你打电话时
AuthenticationMethod.FORMS.ToDescription()
您将获得 "FORMS"
。
using System.ComponentModel;
此外,此方法仅在您希望 String 值与 Enum 的名称相同时才有效。 OP 想要一个不同的值。
AuthenticationMethod.FORMS.ToDescription()
吗?
只需使用 ToString()
方法
public enum any{Tomato=0,Melon,Watermelon}
要引用字符串 Tomato
,只需使用
any.Tomato.ToString();
.ToString()
值与您需要的用户友好值不同。
any fruit = any.Tomato;
string tomato = fruit.ToString();
使用 .Net 4.0 及更高版本可以非常简单地解决此问题。不需要其他代码。
public enum MyStatus
{
Active = 1,
Archived = 2
}
要获取有关的字符串,只需使用:
MyStatus.Active.ToString("f");
或者
MyStatus.Archived.ToString("f");`
该值将是“活动”或“存档”。
要在调用 Enum.ToString
时查看不同的字符串格式(上面的“f”),请参阅此 Enumeration Format Strings 页
我使用 System.ComponentModel 命名空间中的 Description 属性。只需装饰枚举,然后使用此代码检索它:
public static string GetDescription<T>(this object enumerationValue)
where T : struct
{
Type type = enumerationValue.GetType();
if (!type.IsEnum)
{
throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
}
//Tries to find a DescriptionAttribute for a potential friendly name
//for the enum
MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
if (memberInfo != null && memberInfo.Length > 0)
{
object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
{
//Pull out the description value
return ((DescriptionAttribute)attrs[0]).Description;
}
}
//If we have no description attribute, just return the ToString of the enum
return enumerationValue.ToString();
}
举个例子:
public enum Cycle : int
{
[Description("Daily Cycle")]
Daily = 1,
Weekly,
Monthly
}
这段代码很好地迎合了不需要“友好名称”的枚举,并且将只返回枚举的 .ToString() 。
我真的很喜欢 Jakub Šturc 的回答,但缺点是您不能将它与 switch-case 语句一起使用。这是他的答案的略微修改版本,可以与 switch 语句一起使用:
public sealed class AuthenticationMethod
{
#region This code never needs to change.
private readonly string _name;
public readonly Values Value;
private AuthenticationMethod(Values value, String name){
this._name = name;
this.Value = value;
}
public override String ToString(){
return _name;
}
#endregion
public enum Values
{
Forms = 1,
Windows = 2,
SSN = 3
}
public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (Values.Forms, "FORMS");
public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (Values.Windows, "WINDOWS");
public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (Values.SSN, "SSN");
}
因此,您可以获得 Jakub Šturc 答案的所有好处,此外,我们可以将它与 switch 语句一起使用,如下所示:
var authenticationMethodVariable = AuthenticationMethod.FORMS; // Set the "enum" value we want to use.
var methodName = authenticationMethodVariable.ToString(); // Get the user-friendly "name" of the "enum" value.
// Perform logic based on which "enum" value was chosen.
switch (authenticationMethodVariable.Value)
{
case authenticationMethodVariable.Values.Forms: // Do something
break;
case authenticationMethodVariable.Values.Windows: // Do something
break;
case authenticationMethodVariable.Values.SSN: // Do something
break;
}
public static int nextAvailable { get; private set; }
然后在构造函数中 this.Value = nextAvailable++;
=
运算符以允许 switch 工作吗?我在 VB 中做了这个,现在可以在 select case
语句中使用它。
我结合了上面的几个建议,并结合了一些缓存。现在,我从网上某处找到的一些代码中得到了这个想法,但我既不记得我从哪里得到它,也记不起它。因此,如果有人发现类似的东西,请在出处发表评论。
无论如何,使用涉及类型转换器,因此如果您绑定到 UI,它“正常工作”。您可以通过从类型转换器初始化为静态方法来扩展 Jakub 的模式以快速查找代码。
基本用法如下所示
[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]
public enum MyEnum
{
// The custom type converter will use the description attribute
[Description("A custom description")]
ValueWithCustomDescription,
// This will be exposed exactly.
Exact
}
自定义枚举类型转换器的代码如下:
public class CustomEnumTypeConverter<T> : EnumConverter
where T : struct
{
private static readonly Dictionary<T,string> s_toString =
new Dictionary<T, string>();
private static readonly Dictionary<string, T> s_toValue =
new Dictionary<string, T>();
private static bool s_isInitialized;
static CustomEnumTypeConverter()
{
System.Diagnostics.Debug.Assert(typeof(T).IsEnum,
"The custom enum class must be used with an enum type.");
}
public CustomEnumTypeConverter() : base(typeof(T))
{
if (!s_isInitialized)
{
Initialize();
s_isInitialized = true;
}
}
protected void Initialize()
{
foreach (T item in Enum.GetValues(typeof(T)))
{
string description = GetDescription(item);
s_toString[item] = description;
s_toValue[description] = item;
}
}
private static string GetDescription(T optionValue)
{
var optionDescription = optionValue.ToString();
var optionInfo = typeof(T).GetField(optionDescription);
if (Attribute.IsDefined(optionInfo, typeof(DescriptionAttribute)))
{
var attribute =
(DescriptionAttribute)Attribute.
GetCustomAttribute(optionInfo, typeof(DescriptionAttribute));
return attribute.Description;
}
return optionDescription;
}
public override object ConvertTo(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture,
object value, Type destinationType)
{
var optionValue = (T)value;
if (destinationType == typeof(string) &&
s_toString.ContainsKey(optionValue))
{
return s_toString[optionValue];
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture, object value)
{
var stringValue = value as string;
if (!string.IsNullOrEmpty(stringValue) && s_toValue.ContainsKey(stringValue))
{
return s_toValue[stringValue];
}
return base.ConvertFrom(context, culture, value);
}
}
}
MyEnum.ValueWithCustomDescription.??()
还是什么?
在您的问题中,您从未说过您实际上在任何地方都需要枚举的数值。
如果你不需要并且只需要一个字符串类型的枚举(它不是一个整数类型,所以不能是枚举的基础),这是一种方法:
static class AuthenticationMethod
{
public static readonly string
FORMS = "Forms",
WINDOWSAUTHENTICATION = "WindowsAuthentication";
}
您可以使用与 enum 相同的语法来引用它
if (bla == AuthenticationMethod.FORMS)
它会比使用数值(比较字符串而不是数字)慢一点,但从好的方面来说,它不使用反射(慢)来访问字符串。
更新:8 年后访问此页面,在很长一段时间没有接触 C# 之后,看起来我的答案不再是最佳解决方案。我真的很喜欢与属性函数绑定的转换器解决方案。
如果您正在阅读本文,请确保您还查看了其他答案。 (提示:他们在这个之上)
和你们中的大多数人一样,我非常喜欢所选的 answer by Jakub Šturc,但我也非常讨厌复制粘贴代码,并且尽可能少地尝试这样做。
所以我决定我想要一个 EnumBase 类,它的大部分功能都是继承/内置的,让我专注于内容而不是行为。
这种方法的主要问题是基于这样一个事实,即尽管 Enum 值是类型安全的实例,但交互是与 Enum Class 类型的静态实现。所以在泛型魔法的帮助下,我想我终于得到了正确的组合。希望有人发现这和我一样有用。
我将从 Jakub 的示例开始,但使用继承和泛型:
public sealed class AuthenticationMethod : EnumBase<AuthenticationMethod, int>
{
public static readonly AuthenticationMethod FORMS =
new AuthenticationMethod(1, "FORMS");
public static readonly AuthenticationMethod WINDOWSAUTHENTICATION =
new AuthenticationMethod(2, "WINDOWS");
public static readonly AuthenticationMethod SINGLESIGNON =
new AuthenticationMethod(3, "SSN");
private AuthenticationMethod(int Value, String Name)
: base( Value, Name ) { }
public new static IEnumerable<AuthenticationMethod> All
{ get { return EnumBase<AuthenticationMethod, int>.All; } }
public static explicit operator AuthenticationMethod(string str)
{ return Parse(str); }
}
这是基类:
using System;
using System.Collections.Generic;
using System.Linq; // for the .AsEnumerable() method call
// E is the derived type-safe-enum class
// - this allows all static members to be truly unique to the specific
// derived class
public class EnumBase<E, T> where E: EnumBase<E, T>
{
#region Instance code
public T Value { get; private set; }
public string Name { get; private set; }
protected EnumBase(T EnumValue, string Name)
{
Value = EnumValue;
this.Name = Name;
mapping.Add(Name, this);
}
public override string ToString() { return Name; }
#endregion
#region Static tools
static private readonly Dictionary<string, EnumBase<E, T>> mapping;
static EnumBase() { mapping = new Dictionary<string, EnumBase<E, T>>(); }
protected static E Parse(string name)
{
EnumBase<E, T> result;
if (mapping.TryGetValue(name, out result))
{
return (E)result;
}
throw new InvalidCastException();
}
// This is protected to force the child class to expose it's own static
// method.
// By recreating this static method at the derived class, static
// initialization will be explicit, promising the mapping dictionary
// will never be empty when this method is called.
protected static IEnumerable<E> All
{ get { return mapping.Values.AsEnumerable().Cast<E>(); } }
#endregion
}
我如何将其作为扩展方法解决:
using System.ComponentModel;
public static string GetDescription(this Enum value)
{
var descriptionAttribute = (DescriptionAttribute)value.GetType()
.GetField(value.ToString())
.GetCustomAttributes(false)
.Where(a => a is DescriptionAttribute)
.FirstOrDefault();
return descriptionAttribute != null ? descriptionAttribute.Description : value.ToString();
}
枚举:
public enum OrderType
{
None = 0,
[Description("New Card")]
NewCard = 1,
[Description("Reload")]
Refill = 2
}
用法(其中 o.OrderType 是与枚举同名的属性):
o.OrderType.GetDescription()
这给了我一串“新卡”或“重新加载”,而不是实际的枚举值 NewCard 和 Refill。
如果您来这里是为了实现一个简单的“枚举”,但其值是字符串而不是整数,那么这是最简单的解决方案:
public sealed class MetricValueList
{
public static readonly string Brand = "A4082457-D467-E111-98DC-0026B9010912";
public static readonly string Name = "B5B5E167-D467-E111-98DC-0026B9010912";
}
执行:
var someStringVariable = MetricValueList.Brand;
static readonly
更好。
我同意基思的观点,但我(还)不能投票。
我使用静态方法和 swith 语句来准确返回我想要的。在我存储 tinyint 的数据库中,我的代码只使用实际的枚举,所以字符串是针对 UI 要求的。经过多次测试,这导致了最佳性能和对输出的最大控制。
public static string ToSimpleString(this enum)
{
switch (enum)
{
case ComplexForms:
return "ComplexForms";
break;
}
}
public static string ToFormattedString(this enum)
{
switch (enum)
{
case ComplexForms:
return "Complex Forms";
break;
}
}
然而,根据某些说法,这可能会导致维护噩梦和一些代码异味。我试图留意那些很长且有很多枚举的枚举,或者那些经常更改的枚举。否则,这对我来说是一个很好的解决方案。
当我遇到这个问题时,我首先尝试找到几个问题的答案:
我的枚举值的名称是否足够友好,或者我需要提供更友好的名称?
我需要往返吗?也就是说,我需要获取文本值并将它们解析为枚举值吗?
这是我需要为我的项目中的许多枚举做的事情,还是只是一个?
我将在哪种 UI 元素中呈现此信息 - 特别是,我将绑定到 UI 还是使用属性表?
这需要本地化吗?
最简单的方法是使用 Enum.GetValue
(并支持使用 Enum.Parse
进行往返)。正如 Steve Mitcham 建议的那样,通常还值得构建一个 TypeConverter
来支持 UI 绑定。 (当您使用属性表时,没有必要构建 TypeConverter
,这是属性表的优点之一。虽然上帝知道他们有自己的问题。)
一般来说,如果上述问题的答案表明这不起作用,我的下一步是创建并填充静态 Dictionary<MyEnum, string>
,或者可能是 Dictionary<Type, Dictionary<int, string>>
。我倾向于跳过中间的 decorate-the-code-with-attributes 步骤,因为接下来通常需要在部署后更改友好值(通常,但并非总是如此,因为本地化)。
我想将此作为对下面引用的帖子的评论发布,但因为我没有足够的代表而无法发布 - 所以请不要投反对票。该代码包含一个错误,我想向尝试使用此解决方案的个人指出这一点:
[TypeConverter(typeof(CustomEnumTypeConverter(typeof(MyEnum))] public enum MyEnum { // 自定义类型转换器将使用描述属性 [Description("A custom description")] ValueWithCustomDescription, // 这将完全暴露。精确 }
应该
[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]
public enum MyEnum
{
// The custom type converter will use the description attribute
[Description("A custom description")]
ValueWithCustomDescription,
// This will be exposed exactly.
Exact
}
辉煌!
我创建了一个基类,用于在 .NET 中创建字符串值枚举。它只是一个可以复制的 C# 文件 &粘贴到您的项目中,或通过名为 StringEnum 的 NuGet 包安装。 GitHub Repo
如果使用 xml 注释
https://raw.githubusercontent.com/gerardog/StringEnum/master/images/intellisense.gif
用法类似于常规枚举:
///<completionlist cref="HexColor"/>
class HexColor : StringEnum<HexColor>
{
public static readonly HexColor Blue = Create("#FF0000");
public static readonly HexColor Green = Create("#00FF00");
public static readonly HexColor Red = Create("#000FF");
}
// Static Parse Method
HexColor.Parse("#FF0000") // => HexColor.Red
HexColor.Parse("#ff0000", caseSensitive: false) // => HexColor.Red
HexColor.Parse("invalid") // => throws InvalidOperationException
// Static TryParse method.
HexColor.TryParse("#FF0000") // => HexColor.Red
HexColor.TryParse("#ff0000", caseSensitive: false) // => HexColor.Red
HexColor.TryParse("invalid") // => null
// Parse and TryParse returns the preexistent instances
object.ReferenceEquals(HexColor.Parse("#FF0000"), HexColor.Red) // => true
// Conversion from your `StringEnum` to `string`
string myString1 = HexColor.Red.ToString(); // => "#FF0000"
string myString2 = HexColor.Red; // => "#FF0000" (implicit cast)
安装:
将以下 StringEnum 基类粘贴到您的项目中。 (最新版本)
或者安装基于 .Net Standard 1.0 的 StringEnum NuGet 包,因此它可以在 .Net Core >= 1.0、.Net Framework >= 4.5、Mono >= 4.6 等上运行。
/// <summary>
/// Base class for creating string-valued enums in .NET.<br/>
/// Provides static Parse() and TryParse() methods and implicit cast to string.
/// </summary>
/// <example>
/// <code>
/// class Color : StringEnum <Color>
/// {
/// public static readonly Color Blue = Create("Blue");
/// public static readonly Color Red = Create("Red");
/// public static readonly Color Green = Create("Green");
/// }
/// </code>
/// </example>
/// <typeparam name="T">The string-valued enum type. (i.e. class Color : StringEnum<Color>)</typeparam>
public abstract class StringEnum<T> : IEquatable<T> where T : StringEnum<T>, new()
{
protected string Value;
private static Dictionary<string, T> valueDict = new Dictionary<string, T>();
protected static T Create(string value)
{
if (value == null)
return null; // the null-valued instance is null.
var result = new T() { Value = value };
valueDict.Add(value, result);
return result;
}
public static implicit operator string(StringEnum<T> enumValue) => enumValue.Value;
public override string ToString() => Value;
public static bool operator !=(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value != o2?.Value;
public static bool operator ==(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value == o2?.Value;
public override bool Equals(object other) => this.Value.Equals((other as T)?.Value ?? (other as string));
bool IEquatable<T>.Equals(T other) => this.Value.Equals(other.Value);
public override int GetHashCode() => Value.GetHashCode();
/// <summary>
/// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else throws InvalidOperationException.
/// </summary>
/// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>
/// <param name="caseSensitive">If true, the strings must match case and takes O(log n). False allows different case but is little bit slower (O(n))</param>
public static T Parse(string value, bool caseSensitive = true)
{
var result = TryParse(value, caseSensitive);
if (result == null)
throw new InvalidOperationException((value == null ? "null" : $"'{value}'") + $" is not a valid {typeof(T).Name}");
return result;
}
/// <summary>
/// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else returns null.
/// </summary>
/// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>
/// <param name="caseSensitive">If true, the strings must match case. False allows different case but is slower: O(n)</param>
public static T TryParse(string value, bool caseSensitive = true)
{
if (value == null) return null;
if (valueDict.Count == 0) System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); // force static fields initialization
if (caseSensitive)
{
if (valueDict.TryGetValue(value, out T item))
return item;
else
return null;
}
else
{
// slower O(n) case insensitive search
return valueDict.FirstOrDefault(f => f.Key.Equals(value, StringComparison.OrdinalIgnoreCase)).Value;
// Why Ordinal? => https://esmithy.net/2007/10/15/why-stringcomparisonordinal-is-usually-the-right-choice/
}
}
}
我的变种
public struct Colors
{
private String current;
private static string red = "#ff0000";
private static string green = "#00ff00";
private static string blue = "#0000ff";
private static IList<String> possibleColors;
public static Colors Red { get { return (Colors) red; } }
public static Colors Green { get { return (Colors) green; } }
public static Colors Blue { get { return (Colors) blue; } }
static Colors()
{
possibleColors = new List<string>() {red, green, blue};
}
public static explicit operator String(Colors value)
{
return value.current;
}
public static explicit operator Colors(String value)
{
if (!possibleColors.Contains(value))
{
throw new InvalidCastException();
}
Colors color = new Colors();
color.current = value;
return color;
}
public static bool operator ==(Colors left, Colors right)
{
return left.current == right.current;
}
public static bool operator !=(Colors left, Colors right)
{
return left.current != right.current;
}
public bool Equals(Colors other)
{
return Equals(other.current, current);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (obj.GetType() != typeof(Colors)) return false;
return Equals((Colors)obj);
}
public override int GetHashCode()
{
return (current != null ? current.GetHashCode() : 0);
}
public override string ToString()
{
return current;
}
}
代码看起来有点难看,但是这个结构的用法很有代表性。
Colors color1 = Colors.Red;
Console.WriteLine(color1); // #ff0000
Colors color2 = (Colors) "#00ff00";
Console.WriteLine(color2); // #00ff00
// Colors color3 = "#0000ff"; // Compilation error
// String color4 = Colors.Red; // Compilation error
Colors color5 = (Colors)"#ff0000";
Console.WriteLine(color1 == color5); // True
Colors color6 = (Colors)"#00ff00";
Console.WriteLine(color1 == color6); // False
另外,我认为,如果需要大量此类枚举,则可能会使用代码生成(例如 T4)。
选项1:
public sealed class FormsAuth
{
public override string ToString{return "Forms Authtentication";}
}
public sealed class WindowsAuth
{
public override string ToString{return "Windows Authtentication";}
}
public sealed class SsoAuth
{
public override string ToString{return "SSO";}
}
接着
object auth = new SsoAuth(); //or whatever
//...
//...
// blablabla
DoSomethingWithTheAuth(auth.ToString());
选项 2:
public enum AuthenticationMethod
{
FORMS = 1,
WINDOWSAUTHENTICATION = 2,
SINGLESIGNON = 3
}
public class MyClass
{
private Dictionary<AuthenticationMethod, String> map = new Dictionary<AuthenticationMethod, String>();
public MyClass()
{
map.Add(AuthenticationMethod.FORMS,"Forms Authentication");
map.Add(AuthenticationMethod.WINDOWSAUTHENTICATION ,"Windows Authentication");
map.Add(AuthenticationMethod.SINGLESIGNON ,"SSo Authentication");
}
}
如果您考虑一下我们要解决的问题,那根本就不是我们需要的枚举。我们需要一个允许一定数量的值相互关联的对象;换句话说,定义一个类。
Jakub Šturc 的类型安全枚举模式是我在这里看到的最佳选择。
看它:
它有一个私有构造函数,所以只有类本身可以定义允许的值。
它是一个密封类,因此不能通过继承修改值。
它是类型安全的,允许您的方法只需要该类型。
访问这些值不会影响反射性能。
最后,它可以修改为将两个以上的字段关联在一起,例如名称、描述和数值。
对我来说,务实的方法是类内类,示例:
public class MSEModel
{
class WITS
{
public const string DATE = "5005";
public const string TIME = "5006";
public const string MD = "5008";
public const string ROP = "5075";
public const string WOB = "5073";
public const string RPM = "7001";
...
}
这是完成将字符串与枚举关联的任务的另一种方法:
struct DATABASE {
public enum enums {NOTCONNECTED, CONNECTED, ERROR}
static List<string> strings =
new List<string>() {"Not Connected", "Connected", "Error"};
public string GetString(DATABASE.enums value) {
return strings[(int)value];
}
}
这个方法是这样调用的:
public FormMain() {
DATABASE dbEnum;
string enumName = dbEnum.GetString(DATABASE.enums.NOTCONNECTED);
}
您可以将相关的枚举分组到自己的结构中。由于此方法使用枚举类型,因此您可以在调用 GetString()
时使用 Intellisense 显示枚举列表。
您可以选择在 DATABASE
结构上使用 new 运算符。不使用它意味着在第一次调用 GetString()
之前不会分配字符串 List
。
这里有很多很好的答案,但在我的情况下并没有解决我想要的“字符串枚举”,它是:
可以在 switch 语句中使用,例如 switch(myEnum) 可以在函数参数中使用,例如 foo(myEnum type) 可以被引用,例如 myEnum.FirstElement 我可以使用字符串,例如 foo("FirstElement") == foo(myEnum.FirstElement)
1,2 & 4 实际上可以用字符串的 C# Typedef 来解决(因为字符串在 c# 中是可切换的)
3 可以通过静态常量字符串来解决。因此,如果您有相同的需求,这是最简单的方法:
public sealed class Types
{
private readonly String name;
private Types(String name)
{
this.name = name;
}
public override String ToString()
{
return name;
}
public static implicit operator Types(string str)
{
return new Types(str);
}
public static implicit operator string(Types str)
{
return str.ToString();
}
#region enum
public const string DataType = "Data";
public const string ImageType = "Image";
public const string Folder = "Folder";
#endregion
}
这允许例如:
public TypeArgs(Types SelectedType)
{
Types SelectedType = SelectedType
}
和
public TypeObject CreateType(Types type)
{
switch (type)
{
case Types.ImageType:
//
break;
case Types.DataType:
//
break;
}
}
其中 CreateType 可以用字符串或类型调用。然而,缺点是任何字符串都会自动成为有效的枚举,这可以被修改,但它需要某种 init 函数......或者可能使它们显式地转换为内部?
现在,如果 int 值对您很重要(也许是为了比较速度),您可以使用 Jakub Šturc 的一些想法并做一些疯狂的事情,这是我的尝试:
public sealed class Types
{
private static readonly Dictionary<string, Types> strInstance = new Dictionary<string, Types>();
private static readonly Dictionary<int, Types> intInstance = new Dictionary<int, Types>();
private readonly String name;
private static int layerTypeCount = 0;
private int value;
private Types(String name)
{
this.name = name;
value = layerTypeCount++;
strInstance[name] = this;
intInstance[value] = this;
}
public override String ToString()
{
return name;
}
public static implicit operator Types(int val)
{
Types result;
if (intInstance.TryGetValue(val, out result))
return result;
else
throw new InvalidCastException();
}
public static implicit operator Types(string str)
{
Types result;
if (strInstance.TryGetValue(str, out result))
{
return result;
}
else
{
result = new Types(str);
return result;
}
}
public static implicit operator string(Types str)
{
return str.ToString();
}
public static bool operator ==(Types a, Types b)
{
return a.value == b.value;
}
public static bool operator !=(Types a, Types b)
{
return a.value != b.value;
}
#region enum
public const string DataType = "Data";
public const string ImageType = "Image";
#endregion
}
但当然“类型 bob = 4;”除非您首先初始化它们,否则将毫无意义,这会有点失败...
但理论上 TypeA == TypeB 会更快......
如果我对您的理解正确,您可以简单地使用 .ToString() 从值中检索枚举的名称(假设它已经被转换为枚举);如果您有裸 int (可以说来自数据库或其他东西),您可以先将其转换为枚举。以下两种方法都会为您提供枚举名称。
AuthenticationMethod myCurrentSetting = AuthenticationMethod.FORMS;
Console.WriteLine(myCurrentSetting); // Prints: FORMS
string name = Enum.GetNames(typeof(AuthenticationMethod))[(int)myCurrentSetting-1];
Console.WriteLine(name); // Prints: FORMS
但请记住,第二种技术假设您使用的是整数并且您的索引是基于 1 的(而不是基于 0)。相比之下,函数 GetNames 也很重,每次调用它时都会生成一个完整的数组。正如您在第一种技术中看到的那样, .ToString() 实际上是隐式调用的。当然,答案中已经提到了这两者,我只是想澄清它们之间的区别。
旧帖子但是...
这个问题的答案实际上可能非常简单。使用 Enum.ToString() 函数
该函数有 6 个重载,可以使用 Enum.Tostring("F") 或 Enum.ToString() 返回字符串值。无需为其他事情烦恼。这是一个working Demo
请注意,此解决方案可能不适用于所有编译器 (this demo does not work as expected),但至少适用于最新的编译器。
基于 MSDN:http://msdn.microsoft.com/en-us/library/cc138362.aspx
foreach (string str in Enum.GetNames(typeof(enumHeaderField)))
{
Debug.WriteLine(str);
}
str 将是字段的名称
好吧,在阅读了以上所有内容之后,我觉得这些家伙将枚举数转换为字符串的问题过于复杂了。我喜欢在枚举字段上拥有属性的想法,但我认为属性主要用于元数据,但在你的情况下,我认为你所需要的只是某种本地化。
public enum Color
{ Red = 1, Green = 2, Blue = 3}
public static EnumUtils
{
public static string GetEnumResourceString(object enumValue)
{
Type enumType = enumValue.GetType();
string value = Enum.GetName(enumValue.GetType(), enumValue);
string resourceKey = String.Format("{0}_{1}", enumType.Name, value);
string result = Resources.Enums.ResourceManager.GetString(resourceKey);
if (string.IsNullOrEmpty(result))
{
result = String.Format("{0}", value);
}
return result;
}
}
现在,如果我们尝试调用上述方法,我们可以这样调用它
public void Foo()
{
var col = Color.Red;
Console.WriteLine (EnumUtils.GetEnumResourceString (col));
}
您需要做的只是创建一个包含所有枚举器值和相应字符串的资源文件
Resource Name Resource Value Color_Red My String Color in Red Color_Blue Blueeey Color_Green Hulk Color
实际上非常好的一点是,如果您需要本地化您的应用程序,这将非常有帮助,因为您需要做的只是用您的新语言创建另一个资源文件!和沃拉!
当我处于这种情况时,我会提出以下解决方案。
作为一个消费阶层,你可以拥有
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyApp.Dictionaries
{
class Greek
{
public static readonly string Alpha = "Alpha";
public static readonly string Beta = "Beta";
public static readonly string Gamma = "Gamma";
public static readonly string Delta = "Delta";
private static readonly BiDictionary<int, string> Dictionary = new BiDictionary<int, string>();
static Greek() {
Dictionary.Add(1, Alpha);
Dictionary.Add(2, Beta);
Dictionary.Add(3, Gamma);
Dictionary.Add(4, Delta);
}
public static string getById(int id){
return Dictionary.GetByFirst(id);
}
public static int getByValue(string value)
{
return Dictionary.GetBySecond(value);
}
}
}
并使用双向字典:基于此 (https://stackoverflow.com/a/255638/986160) 假设键将与字典中的单个值相关联并且类似于 (https://stackoverflow.com/a/255630/986160) 但更优雅一点。这本字典也是可枚举的,你可以从整数到字符串来回切换。此外,除了此类之外,您的代码库中不必有任何字符串。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace MyApp.Dictionaries
{
class BiDictionary<TFirst, TSecond> : IEnumerable
{
IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();
public void Add(TFirst first, TSecond second)
{
firstToSecond.Add(first, second);
secondToFirst.Add(second, first);
}
public TSecond this[TFirst first]
{
get { return GetByFirst(first); }
}
public TFirst this[TSecond second]
{
get { return GetBySecond(second); }
}
public TSecond GetByFirst(TFirst first)
{
return firstToSecond[first];
}
public TFirst GetBySecond(TSecond second)
{
return secondToFirst[second];
}
public IEnumerator GetEnumerator()
{
return GetFirstEnumerator();
}
public IEnumerator GetFirstEnumerator()
{
return firstToSecond.GetEnumerator();
}
public IEnumerator GetSecondEnumerator()
{
return secondToFirst.GetEnumerator();
}
}
}
对于较大的字符串枚举集,列出的示例可能会变得令人厌烦。如果你想要一个状态码列表,或者其他基于字符串的枚举列表,属性系统使用起来很烦人,而带有自身实例的静态类配置起来很烦人。对于我自己的解决方案,我使用 T4 模板来简化字符串支持的枚举。结果类似于 HttpMethod 类的工作方式。
你可以像这样使用它:
string statusCode = ResponseStatusCode.SUCCESS; // Automatically converts to string when needed
ResponseStatusCode codeByValueOf = ResponseStatusCode.ValueOf(statusCode); // Returns null if not found
// Implements TypeConverter so you can use it with string conversion methods.
var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(ResponseStatusCode));
ResponseStatusCode code = (ResponseStatusCode) converter.ConvertFromInvariantString(statusCode);
// You can get a full list of the values
bool canIterateOverValues = ResponseStatusCode.Values.Any();
// Comparisons are by value of the "Name" property. Not by memory pointer location.
bool implementsByValueEqualsEqualsOperator = "SUCCESS" == ResponseStatusCode.SUCCESS;
您从 Enum.tt 文件开始。
<#@ include file="StringEnum.ttinclude" #>
<#+
public static class Configuration
{
public static readonly string Namespace = "YourName.Space";
public static readonly string EnumName = "ResponseStatusCode";
public static readonly bool IncludeComments = true;
public static readonly object Nodes = new
{
SUCCESS = "The response was successful.",
NON_SUCCESS = "The request was not successful.",
RESOURCE_IS_DISCONTINUED = "The resource requested has been discontinued and can no longer be accessed."
};
}
#>
然后,添加 StringEnum.ttinclude 文件。
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#@ CleanupBehavior processor="T4VSHost" CleanupAfterProcessingtemplate="true" #>
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
namespace <#= Configuration.Namespace #>
{
/// <summary>
/// TypeConverter implementations allow you to use features like string.ToNullable(T).
/// </summary>
public class <#= Configuration.EnumName #>TypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var casted = value as string;
if (casted != null)
{
var result = <#= Configuration.EnumName #>.ValueOf(casted);
if (result != null)
{
return result;
}
}
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
var casted = value as <#= Configuration.EnumName #>;
if (casted != null && destinationType == typeof(string))
{
return casted.ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
[TypeConverter(typeof(<#= Configuration.EnumName #>TypeConverter))]
public class <#= Configuration.EnumName #> : IEquatable<<#= Configuration.EnumName #>>
{
//---------------------------------------------------------------------------------------------------
// V A L U E S _ L I S T
//---------------------------------------------------------------------------------------------------
<# Write(Helpers.PrintEnumProperties(Configuration.Nodes)); #>
private static List<<#= Configuration.EnumName #>> _list { get; set; } = null;
public static List<<#= Configuration.EnumName #>> ToList()
{
if (_list == null)
{
_list = typeof(<#= Configuration.EnumName #>).GetFields().Where(x => x.IsStatic && x.IsPublic && x.FieldType == typeof(<#= Configuration.EnumName #>))
.Select(x => x.GetValue(null)).OfType<<#= Configuration.EnumName #>>().ToList();
}
return _list;
}
public static List<<#= Configuration.EnumName #>> Values()
{
return ToList();
}
/// <summary>
/// Returns the enum value based on the matching Name of the enum. Case-insensitive search.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static <#= Configuration.EnumName #> ValueOf(string key)
{
return ToList().FirstOrDefault(x => string.Compare(x.Name, key, true) == 0);
}
//---------------------------------------------------------------------------------------------------
// I N S T A N C E _ D E F I N I T I O N
//---------------------------------------------------------------------------------------------------
public string Name { get; private set; }
public string Description { get; private set; }
public override string ToString() { return this.Name; }
/// <summary>
/// Implcitly converts to string.
/// </summary>
/// <param name="d"></param>
public static implicit operator string(<#= Configuration.EnumName #> d)
{
return d.ToString();
}
/// <summary>
/// Compares based on the == method. Handles nulls gracefully.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator !=(<#= Configuration.EnumName #> a, <#= Configuration.EnumName #> b)
{
return !(a == b);
}
/// <summary>
/// Compares based on the .Equals method. Handles nulls gracefully.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator ==(<#= Configuration.EnumName #> a, <#= Configuration.EnumName #> b)
{
return a?.ToString() == b?.ToString();
}
/// <summary>
/// Compares based on the .ToString() method
/// </summary>
/// <param name="o"></param>
/// <returns></returns>
public override bool Equals(object o)
{
return this.ToString() == o?.ToString();
}
/// <summary>
/// Compares based on the .ToString() method
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public bool Equals(<#= Configuration.EnumName #> other)
{
return this.ToString() == other?.ToString();
}
/// <summary>
/// Compares based on the .Name property
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return this.Name.GetHashCode();
}
}
}
<#+
public static class Helpers
{
public static string PrintEnumProperties(object nodes)
{
string o = "";
Type nodesTp = Configuration.Nodes.GetType();
PropertyInfo[] props = nodesTp.GetProperties().OrderBy(p => p.Name).ToArray();
for(int i = 0; i < props.Length; i++)
{
var prop = props[i];
if (Configuration.IncludeComments)
{
o += "\r\n\r\n";
o += "\r\n ///<summary>";
o += "\r\n /// "+Helpers.PrintPropertyValue(prop, Configuration.Nodes);
o += "\r\n ///</summary>";
}
o += "\r\n public static readonly "+Configuration.EnumName+" "+prop.Name+ " = new "+Configuration.EnumName+"(){ Name = \""+prop.Name+"\", Description = "+Helpers.PrintPropertyValue(prop, Configuration.Nodes)+ "};";
}
o += "\r\n\r\n";
return o;
}
private static Dictionary<string, string> GetValuesMap()
{
Type nodesTp = Configuration.Nodes.GetType();
PropertyInfo[] props= nodesTp.GetProperties();
var dic = new Dictionary<string,string>();
for(int i = 0; i < props.Length; i++)
{
var prop = nodesTp.GetProperties()[i];
dic[prop.Name] = prop.GetValue(Configuration.Nodes).ToString();
}
return dic;
}
public static string PrintMasterValuesMap(object nodes)
{
Type nodesTp = Configuration.Nodes.GetType();
PropertyInfo[] props= nodesTp.GetProperties();
string o = " private static readonly Dictionary<string, string> ValuesMap = new Dictionary<string, string>()\r\n {";
for(int i = 0; i < props.Length; i++)
{
var prop = nodesTp.GetProperties()[i];
o += "\r\n { \""+prop.Name+"\", "+(Helpers.PrintPropertyValue(prop,Configuration.Nodes)+" },");
}
o += ("\r\n };\r\n");
return o;
}
public static string PrintPropertyValue(PropertyInfo prop, object objInstance)
{
switch(prop.PropertyType.ToString()){
case "System.Double":
return prop.GetValue(objInstance).ToString()+"D";
case "System.Float":
return prop.GetValue(objInstance).ToString()+"F";
case "System.Decimal":
return prop.GetValue(objInstance).ToString()+"M";
case "System.Long":
return prop.GetValue(objInstance).ToString()+"L";
case "System.Boolean":
case "System.Int16":
case "System.Int32":
return prop.GetValue(objInstance).ToString().ToLowerInvariant();
case "System.String":
return "\""+prop.GetValue(objInstance)+"\"";
}
return prop.GetValue(objInstance).ToString();
}
public static string _ (int numSpaces)
{
string o = "";
for(int i = 0; i < numSpaces; i++){
o += " ";
}
return o;
}
}
#>
最后,重新编译 Enum.tt 文件,输出如下所示:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Linq;
using System.Collections.Generic;
namespace YourName.Space
{
public class ResponseStatusCode
{
//---------------------------------------------------------------------------------------------------
// V A L U E S _ L I S T
//---------------------------------------------------------------------------------------------------
///<summary>
/// "The response was successful."
///</summary>
public static readonly ResponseStatusCode SUCCESS = new ResponseStatusCode(){ Name = "SUCCESS", Description = "The response was successful."};
///<summary>
/// "The request was not successful."
///</summary>
public static readonly ResponseStatusCode NON_SUCCESS = new ResponseStatusCode(){ Name = "NON_SUCCESS", Description = "The request was not successful."};
///<summary>
/// "The resource requested has been discontinued and can no longer be accessed."
///</summary>
public static readonly ResponseStatusCode RESOURCE_IS_DISCONTINUED = new ResponseStatusCode(){ Name = "RESOURCE_IS_DISCONTINUED", Description = "The resource requested has been discontinued and can no longer be accessed."};
private static List<ResponseStatusCode> _list { get; set; } = null;
public static List<ResponseStatusCode> ToList()
{
if (_list == null)
{
_list = typeof(ResponseStatusCode).GetFields().Where(x => x.IsStatic && x.IsPublic && x.FieldType == typeof(ResponseStatusCode))
.Select(x => x.GetValue(null)).OfType<ResponseStatusCode>().ToList();
}
return _list;
}
public static List<ResponseStatusCode> Values()
{
return ToList();
}
/// <summary>
/// Returns the enum value based on the matching Name of the enum. Case-insensitive search.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static ResponseStatusCode ValueOf(string key)
{
return ToList().FirstOrDefault(x => string.Compare(x.Name, key, true) == 0);
}
//---------------------------------------------------------------------------------------------------
// I N S T A N C E _ D E F I N I T I O N
//---------------------------------------------------------------------------------------------------
public string Name { get; set; }
public string Description { get; set; }
public override string ToString() { return this.Name; }
/// <summary>
/// Implcitly converts to string.
/// </summary>
/// <param name="d"></param>
public static implicit operator string(ResponseStatusCode d)
{
return d.ToString();
}
/// <summary>
/// Compares based on the == method. Handles nulls gracefully.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator !=(ResponseStatusCode a, ResponseStatusCode b)
{
return !(a == b);
}
/// <summary>
/// Compares based on the .Equals method. Handles nulls gracefully.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator ==(ResponseStatusCode a, ResponseStatusCode b)
{
return a?.ToString() == b?.ToString();
}
/// <summary>
/// Compares based on the .ToString() method
/// </summary>
/// <param name="o"></param>
/// <returns></returns>
public override bool Equals(object o)
{
return this.ToString() == o?.ToString();
}
/// <summary>
/// Compares based on the .Name property
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return this.Name.GetHashCode();
}
}
}
不定期副业成功案例分享