如何定义具有字符串值的枚举?

我试图定义一个 Enum和添加有效的公共分隔符使用在 CSV 或类似的文件。然后我将它绑定到一个 ComboBox作为数据源,所以无论何时我添加或从 Enum 定义中删除,我都不需要在组合框中更改任何内容。

问题是如何使用字符串表示来定义枚举,比如:

public enum SeparatorChars{Comma = ",", Tab = "\t", Space = " "}

565686 次浏览

据我所知,您将不被允许将字符串值赋给枚举。您可以做的是创建一个包含字符串常量的类。

public static class SeparatorChars
{
public static String Comma { get { return ",";} }
public static String Tab { get { return "\t,";} }
public static String Space { get { return " ";} }
}

你不能用 enum 做这个,但是你可以这样做:

public static class SeparatorChars
{
public static string Comma = ",";


public static string Tab = "\t";


public static string Space = " ";
}

你不能-枚举值必须是整数值。您可以使用属性将字符串值与每个枚举值关联起来,或者在这种情况下,如果每个分隔符都是一个字符,那么您可以只使用 char值:

enum Separator
{
Comma = ',',
Tab = '\t',
Space = ' '
}

(编辑: 澄清一下,您不能使 char成为枚举的基础类型,但是您可以使用 char常量来分配与每个枚举值对应的整数值。上述枚举的基础类型是 int。)

如果需要的话,可以使用扩展方法:

public string ToSeparatorString(this Separator separator)
{
// TODO: validation
return ((char) separator).ToString();
}

首先尝试赋值字符串而不是字符,即使它们只是一个字符。使用’,’而不是’,’。接下来的事情是,枚举只采用没有 char的整数类型,你可以使用 unicode 值,但我强烈建议你不要这样做。 如果您确定这些值在不同的区域性和语言中保持不变,我将使用带常量字符串的静态类。

不能,因为枚举只能基于基元数值类型。 你可以尝试使用 Dictionary代替:

Dictionary<String, char> separators = new Dictionary<string, char>
{
{"Comma", ','},
{"Tab",  '\t'},
{"Space", ' '},
};

或者,您可以使用 Dictionary<Separator, char>Dictionary<Separator, string>,其中 Separator是一个普通枚举:

enum Separator
{
Comma,
Tab,
Space
}

这比直接处理字符串要好一些。

你可以做到这一点,但这需要一些工作。

  1. 定义一个包含枚举的字符串值的属性类。

  2. 定义一个扩展方法,它将返回属性的值。例如,GetStringValue(this Enum value)将返回属性值。

  3. 然后您可以像这样定义枚举。

    public enum Test : int {
    [StringValue("a")]
    Foo = 1,
    [StringValue("b")]
    Something = 2
    }
    
  4. 从 Attribute Test.Foo.GetStringValue();返回值

参考: 在 C # 中使用字符串值的枚举

虽然实际上不可能使用 charstring作为枚举的基础,但我认为这不是您真正喜欢做的事情。

正如您提到的,您希望拥有一个枚举的可能性,并在组合框中显示这个枚举的字符串表示形式。如果用户选择了这些字符串表示形式中的一种,那么您希望得到相应的枚举。这是可能的:

首先,我们必须将一些字符串链接到一个枚举值。这可以通过像描述 给你给你那样使用 DescriptionAttribute来完成。

现在您需要创建一个枚举值列表和相应的描述。这可以通过以下方法实现:

/// <summary>
/// Creates an List with all keys and values of a given Enum class
/// </summary>
/// <typeparam name="T">Must be derived from class Enum!</typeparam>
/// <returns>A list of KeyValuePair&lt;Enum, string&gt; with all available
/// names and values of the given Enum.</returns>
public static IList<KeyValuePair<T, string>> ToList<T>() where T : struct
{
var type = typeof(T);


if (!type.IsEnum)
{
throw new ArgumentException("T must be an enum");
}


return (IList<KeyValuePair<T, string>>)
Enum.GetValues(type)
.OfType<T>()
.Select(e =>
{
var asEnum = (Enum)Convert.ChangeType(e, typeof(Enum));
return new KeyValuePair<T, string>(e, asEnum.Description());
})
.ToArray();
}

现在您将有一个所有枚举的键值对及其描述的列表。因此,让我们简单地将它分配为一个组合框的数据源。

var comboBox = new ComboBox();
comboBox.ValueMember = "Key"
comboBox.DisplayMember = "Value";
comboBox.DataSource = EnumUtilities.ToList<Separator>();


comboBox.SelectedIndexChanged += (sender, e) =>
{
var selectedEnum = (Separator)comboBox.SelectedValue;
MessageBox.Show(selectedEnum.ToString());
}

用户可以看到枚举的所有字符串表示形式,在代码中您将获得所需的枚举值。

枚举类

 public sealed class GenericDateTimeFormatType
{


public static readonly GenericDateTimeFormatType Format1 = new GenericDateTimeFormatType("dd-MM-YYYY");
public static readonly GenericDateTimeFormatType Format2 = new GenericDateTimeFormatType("dd-MMM-YYYY");


private GenericDateTimeFormatType(string Format)
{
_Value = Format;
}


public string _Value { get; private set; }
}

枚举消耗

public static void Main()
{
Country A = new Country();


A.DefaultDateFormat = GenericDateTimeFormatType.Format1;


Console.ReadLine();
}

我们不能将枚举定义为字符串类型。枚举的批准类型有 byte、 sbyte、 short、 usshort、 int、 uint、 long 或 ulong。

如果你需要更多关于枚举的详细信息,请点击下面的链接,该链接将帮助你理解枚举。 枚举

@ narendras1414

对于字符串值(或任何其他类型)的简单枚举:

public static class MyEnumClass
{
public const string
MyValue1 = "My value 1",
MyValue2 = "My value 2";
}

用法: < code > string MyValue = MyEnumClass.MyValue1;

模拟枚举行为但使用 string而不是 int的类可以创建如下..。

public class GrainType
{
private string _typeKeyWord;


private GrainType(string typeKeyWord)
{
_typeKeyWord = typeKeyWord;
}


public override string ToString()
{
return _typeKeyWord;
}


public static GrainType Wheat = new GrainType("GT_WHEAT");
public static GrainType Corn = new GrainType("GT_CORN");
public static GrainType Rice = new GrainType("GT_RICE");
public static GrainType Barley = new GrainType("GT_BARLEY");


}

用法..。

GrainType myGrain = GrainType.Wheat;


PrintGrainKeyword(myGrain);

然后..。

public void PrintGrainKeyword(GrainType grain)
{
Console.Writeline("My Grain code is " + grain.ToString());   // Displays "My Grain code is GT_WHEAT"
}

现在回答这个问题有点晚了,但也许将来会对某人有所帮助。我发现用 struct 解决这类问题更容易。

下面的例子是从 MS 代码复制粘贴的部分:

namespace System.IdentityModel.Tokens.Jwt
{
//
// Summary:
//     List of registered claims from different sources http://tools.ietf.org/html/rfc7519#section-4
//     http://openid.net/specs/openid-connect-core-1_0.html#IDToken
public struct JwtRegisteredClaimNames
{
//
// Summary:
//     http://tools.ietf.org/html/rfc7519#section-4
public const string Actort = "actort";
//
// Summary:
//     http://tools.ietf.org/html/rfc7519#section-4
public const string Typ = "typ";
//
// Summary:
//     http://tools.ietf.org/html/rfc7519#section-4
public const string Sub = "sub";
//
// Summary:
//     http://openid.net/specs/openid-connect-frontchannel-1_0.html#OPLogout
public const string Sid = "sid";
//
// Summary:
//     http://tools.ietf.org/html/rfc7519#section-4
public const string Prn = "prn";
//
// Summary:
//     http://tools.ietf.org/html/rfc7519#section-4
public const string Nbf = "nbf";
//
// Summary:
//     http://tools.ietf.org/html/rfc7519#section-4
public const string Nonce = "nonce";
//
// Summary:
//     http://tools.ietf.org/html/rfc7519#section-4
public const string NameId = "nameid";


}
}

基于这里的一些答案,我实现了一个可重用的基类,它模拟枚举的行为,但是以 string作为底层类型。它支持各种业务,包括:

  1. 得到一个可能值的列表
  2. 转换为字符串
  3. 通过 .Equals==!=与其他实例进行比较
  4. 使用 JSON.NET JsonConverter 转换到/从 JSON

这是整个基类:

public abstract class StringEnumBase<T> : IEquatable<T>
where T : StringEnumBase<T>
{
public string Value { get; }


protected StringEnumBase(string value) => this.Value = value;


public override string ToString() => this.Value;


public static List<T> AsList()
{
return typeof(T)
.GetProperties(BindingFlags.Public | BindingFlags.Static)
.Where(p => p.PropertyType == typeof(T))
.Select(p => (T)p.GetValue(null))
.ToList();
}


public static T Parse(string value)
{
List<T> all = AsList();


if (!all.Any(a => a.Value == value))
throw new InvalidOperationException($"\"{value}\" is not a valid value for the type {typeof(T).Name}");


return all.Single(a => a.Value == value);
}


public bool Equals(T other)
{
if (other == null) return false;
return this.Value == other?.Value;
}


public override bool Equals(object obj)
{
if (obj == null) return false;
if (obj is T other) return this.Equals(other);
return false;
}


public override int GetHashCode() => this.Value.GetHashCode();


public static bool operator ==(StringEnumBase<T> a, StringEnumBase<T> b) => a?.Equals(b) ?? false;


public static bool operator !=(StringEnumBase<T> a, StringEnumBase<T> b) => !(a?.Equals(b) ?? false);


public class JsonConverter<T> : Newtonsoft.Json.JsonConverter
where T : StringEnumBase<T>
{
public override bool CanRead => true;


public override bool CanWrite => true;


public override bool CanConvert(Type objectType) => ImplementsGeneric(objectType, typeof(StringEnumBase<>));


private static bool ImplementsGeneric(Type type, Type generic)
{
while (type != null)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == generic)
return true;


type = type.BaseType;
}


return false;
}


public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken item = JToken.Load(reader);
string value = item.Value<string>();
return StringEnumBase<T>.Parse(value);
}


public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is StringEnumBase<T> v)
JToken.FromObject(v.Value).WriteTo(writer);
}
}
}

这就是你如何实现你的“字符串枚举”:

[JsonConverter(typeof(JsonConverter<Colour>))]
public class Colour : StringEnumBase<Colour>
{
private Colour(string value) : base(value) { }


public static Colour Red => new Colour("red");
public static Colour Green => new Colour("green");
public static Colour Blue => new Colour("blue");
}

可以这样使用:

public class Foo
{
public Colour colour { get; }


public Foo(Colour colour) => this.colour = colour;


public bool Bar()
{
if (this.colour == Colour.Red || this.colour == Colour.Blue)
return true;
else
return false;
}
}

我希望有人觉得这有用!

对于到这里来寻找更一般问题答案的人来说,如果希望代码看起来像 enum,那么可以扩展静态类的概念。

下面的方法在你还没有完成你想要的 enum names,而 enum valuesenam namestring表示的时候起作用; 使用 nameof()使你的重构更简单。

public static class Colours
{
public static string Red => nameof(Red);
public static string Green => nameof(Green);
public static string Blue => nameof(Blue);
}

这实现了具有字符串值的枚举的意图(如下面的伪代码) :

public enum Colours
{
"Red",
"Green",
"Blue"
}

对我有用。

   public class ShapeTypes
{
private ShapeTypes() { }
public static string OVAL
{
get
{
return "ov";
}
private set { }
}


public static string SQUARE
{
get
{
return "sq";
}
private set { }
}


public static string RECTANGLE
{
get
{
return "rec";
}
private set { }
}
}

我最近开始使用 元组

public static (string Fox, string Rabbit, string Horse) Animals = ("Fox", "Rabbit", "Horse");
...
public static (string Comma, string Tab, string Space) SeparatorChars = (",", "\t", " ");

中创建字符串值枚举的基类。NET.它只是一个 C # 文件,您可以复制粘贴到您的项目,或通过名为 StringEnum的 NuGet 包安装。

用法:

///<completionlist cref="HexColor"/>
class HexColor : StringEnum<HexColor>
{
public static readonly HexColor Blue = New("#FF0000");
public static readonly HexColor Green = New("#00FF00");
public static readonly HexColor Red = New("#000FF");
}

特征

  • 您的 StringEnum 看起来有点类似于常规的 enum:
    // 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)
  • 如果类是用 xml 注释 <completitionlist>注释的,Intellisense 将建议枚举名称。(同时适用于 C # 和 VB) : 即。

Intellisense demo

安装

或者:

  • 安装最新的基于 .Net Standard 1.0< strong > StringEnum NuGet 软件包,使其在 .Net Core > = 1.0、 .Net Framework > = 4.5、 Mono > = 4.6等环境下运行。
  • 或者将下面的 StringEnum 基类粘贴到项目中
    public abstract class StringEnum<T> : IEquatable<T> where T : StringEnum<T>, new()
{
protected string Value;
private static IList<T> valueList = new List<T>();
protected static T New(string value)
{
if (value == null)
return null; // the null-valued instance is null.


var result = new T() { Value = value };
valueList.Add(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 sensitivity.</param>
public static T Parse(string value, bool caseSensitive = false)
{
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 sensitivity.</param>
public static T TryParse(string value, bool caseSensitive = false)
{
if (value == null) return null;
if (valueList.Count == 0) System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); // force static fields initialization
var field = valueList.FirstOrDefault(f => f.Value.Equals(value,
caseSensitive ? StringComparison.Ordinal
: StringComparison.OrdinalIgnoreCase));
// Not using InvariantCulture because it's only supported in NETStandard >= 2.0


if (field == null)
return null;


return field;
}
}
  • 要获得对 Newtonsoft.Json序列化的支持,请复制此扩展版本

后来我意识到这个代码和本的答案很相似。我真诚地从头开始写。然而,我认为它还有一些额外的功能,比如 <completitionlist>黑客技术,生成的类看起来更像一个 Enum,没有使用 Parse ()、 NuGet 包和 repo 的反射,我希望在这里解决引入的问题和反馈。

也许已经太迟了,但事情是这样的。

我们可以使用属性 EnumMember 来管理 Enum 值。

public enum UnitOfMeasure
{
[EnumMember(Value = "KM")]
Kilometer,
[EnumMember(Value = "MI")]
Miles
}

这样,UnitOfScale 的结果值将是 KM 或 MI。这也可以在安德鲁惠特克 回答中看到。

我希望有一个更优雅的解决方案,像只允许字符串类型 enum在语言级别,但似乎它还没有得到支持。下面的代码基本上与其他答案相同,但我认为它更短,可以重用。您所要做的就是在每个 enum条目上添加一个 [Description("")],并添加一个有10行的类。

课程:

public static class Extensions
{
public static string ToStringValue(this Enum en)
{
var type = en.GetType();
var memInfo = type.GetMember(en.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
var stringValue = ((DescriptionAttribute)attributes[0]).Description;
return stringValue;
}
}

用法:

    enum Country
{
[Description("Deutschland")]
Germany,
[Description("Nippon")]
Japan,
[Description("Italia")]
Italy,
}


static void Main(string[] args)
{
Show(new[] {Country.Germany, Country.Japan, Country.Italy});


void Show(Country[] countries)
{
foreach (var country in countries)
{
Debug.WriteLine(country.ToStringValue());
}
}
}

由于这里的一些答案忽略了使用枚举的意义,因此还需要添加一些内容。当然,其中一种选择是将定义良好的字符串作为静态变量等,但这样做也会打开接口以获取非法值,也就是说,您需要验证输入。通过枚举,可以保证只有允许的值被传递到接口。

enum Separator
{
Comma,
Tab,
Space,
CRLF,
SoFunny
}

除此之外,您还可以使用内部 Dictionary 进行映射。

    private readonly Dictionary<Separator, string> separatorMap = new Dictionary<Separator, string>()
{
{ Separator.Comma, "," },
{ Separator.Tab, "\t" },
{ Separator.Space, " " },
{ Separator.CRLF, "\r\n" },
{ Separator.SoFunny, "Your Mom" }
};

实现这一点的更复杂的方法是创建一个 静态类,为枚举提供新的能力并处理那里的映射。

使用上面代码的示例如下。

public string TransformToSingleString(List<string> values, Separator separator)
{
var separateWith = separatorMap[separator];
...
}