【问题标题】:Enum.Parse method returns a different enum member than the one passed into it as the parameterEnum.Parse 方法返回的枚举成员与作为参数传递给它的枚举成员不同
【发布时间】:2013-12-14 05:42:06
【问题描述】:

我希望对 Parse 方法的以下调用返回枚举成员 EN_CA,但它返回的是 EN。

Enum.Parse(LanguageType, "EN_CA", true);

地点:

enum LanguageType
{
  EN = 0,
  EN_CA = 0,
  FR = 1
}

Parse 方法似乎只是抓取了第一个映射到传递给它的参数值的成员。Parse 方法似乎在从字符串到枚举成员的转换过程中丢失了信息。更改 EN_CA 和 EN 的顺序将使上述调用返回 EN_CA 但这并不能解决问题,因为在调用“EN”的方法时会导致类似的问题。

其他上下文相关信息:在我的代码中,LanguageType Enum 表示字典的索引,这些索引用于本地化语言。

有没有人知道如何将两个枚举成员映射到相同的值,同时能够从 Enum.Parse 获取正确的成员?我是否只需要实现我自己的 Enum.Parse 版本,它不会将具有相同值的成员合并为一个?

【问题讨论】:

  • 为什么ENEN_CA 使用相同的值?
  • 整数值 0 映射到某个索引,我想让 EN 和 EN_CA 都映射到同一个索引。我的代码中的索引 0 为某些字典带来了英文翻译;所以我想让 myDictionary[LanguageType.EN] 和 myDictionary[LanguageType.EN_CA] 指向同一个东西。
  • 它是有效的,但会使您的代码无法按设计运行。

标签: c# enums


【解决方案1】:

一个枚举成员不同于其他枚举成员如果且如果它具有不同的值。实际上,枚举的成员被命名为常量,除了它们所持有的值之外,它们之间没有真正的区别。

在您的 LanguageType 枚举中,您有两个相同值的标签,无法区分它们。试试这个:

Console.WriteLine("{0} == {1} ? {2}", 
    LanguageType.EN.ToString(), 
    LanguageType.EN_CA.ToString(),
    LanguageType.EN == LanguageType.EN_CA);

输出是:

EN == EN ? True

当您将LanguageType.EN_CA 分配给一个变量时,也会发生同样的事情,然后稍后检查该变量以查看它包含的内容。你得到的是LanguageType.EN

这里的关键是成员的 在大多数情况下是最重要的,成员的位置 在碰撞期间是决胜局。当两个成员具有相同的值时,首先声明的成员就是您在进行字符串转换时会看到的成员,包括在 IDE 中检查值时。

所以实际上你有一个值LanguageType.EN_CA,它只是值LanguageType.EN的别名。

虽然我可以想到几个可爱的用途 - 例如解析具有相同值的多个表示的传入数据 - 这在大多数现实世界中确实是一件非常糟糕的事情环境,尤其是如果您希望能够进行完全对称的序列化/反序列化。

现在,关于您的本地化Dictionary...

在字典中多次存储同一个类对象的成本很低,因为类对象是通过引用存储的。假设你有一个Localization 类,下面的代码在存储方面并不是特别低效:

enum LanguageType
{
    EN, EN_CA, EN_US, EN_GB, EN_AU, FR
}


Dictionary<LanguageType, Localization> localizations = new Dictionary<LanguageType, Localization>();

localizations[LanguageType.EN] = new Localization("EN");
localizations[LanguageType.EN_CA] = localizations[LanguageType.EN];
localizations[LanguageType.EN_US] = localizations[LanguageType.EN];
localizations[LanguageType.EN_GB] = localizations[LanguageType.EN];
localizations[LanguageType.EN_AU] = localizations[LanguageType.EN];

即使Localization 对象包含大量资源,上面的代码也只会创建它的一个实例。您会获得 LanguageType 成员的不同值,而 Dictionary 会为您完成映射。

【讨论】:

  • 这归结为手动重建 .Net 运行时中已经存在的东西。
  • @flup 怎么会这样?如果您只是在谈论我们正在讨论本地化这一事实,那么您的关注范围太窄了。如果枚举被称为SupplierType 并且字典包含PaymentTerms 对象怎么办?这会重新实现.NET 中内置的东西吗?如果是这样,也许你可以解释得更好一点。
  • 问题中给出了上下文,它是国际化的上下文。
  • @flup 问题是关于枚举的。恰好问题的详细信息是关于本地化的。枚举方面的范围更广,应该这样处理。除非您知道 Greenish 使用它来保存.NET 本地化代码可以直接支持的信息,否则您无法做出任何真正的决定。我并不想无礼,但回答实际提出的问题似乎很重要。
  • 另一方面,我们不能创造性地帮助他将方钉锤入圆孔愉快无论如何,提出了很多建议,他会得到足够的可能的解决方案的图片。对不起,如果我让你不高兴了,这不是故意的。
【解决方案2】:

您在枚举中定义了两次0 - 这不起作用。每个值都必须是唯一的,否则枚举将如何识别其值 - 它实际上存储为整数。

改成:

enum LanguageType
{
  EN = 0,
  EN_CA = 1,
  FR = 2
}

编辑

正如 Greenish 所指出的,您可以为同一个值定义多个名称,作为一种别名。使用两个或多个名称会返回相同的值。如果您尝试从整数值中获取字符串值,您将获得为该整数定义的第一个值。

在您的情况下,您无法使用枚举实现所需的功能。您可能应该构建自己的自定义类来实现这一点。

【讨论】:

  • 这是定义枚举的有效方式。请看this
  • @Greenish 是的,我会将其添加到答案中。但这并没有改变 OP 的问题。
  • 好吧,一开始我有点误解了。你的字典的键是int吗?如果是,它已经可以正常工作了,不是吗?
【解决方案3】:

枚举基本上被命名为整数。并且可以为单个数字分配不同的名称(如您的示例中的 0)。

Enum.Parse 正在搜索第一个正确的实例 - 在这里您有一个有效的名称 0 == 0。所以解决方案是改变你的数字(或者甚至把它们去掉,因为它们是 0、1、2)

enum LanguageType
{
  EN,
  EN_CA,
  FR
}

【讨论】:

    【解决方案4】:

    我想了一个办法,但它很乱,很脆弱,很糟糕。它再次说明了您为自己挖的洞..

    int index = Enum.GetNames(typeof(LanguageType)).indexof("EN_CA")
    

    会给你1

    然后类似

    switch(index)
    {
      case 0 : return LanguageType.EN;
      case 1 : return LangaugeType.EN_CA;
      case 2 : return LanguageType.FR;
      default : // throw some useful exception maybe
       break
    }
    

    将返回您想要的成员。

    可怕吗?

    Enum.GetValues(typeof(LanguageType)) 将返回 [0,0,1] 并在其上使用 indexof 0 当然是 0,这就是 Parse 为您提供结果的原因。

    【讨论】:

    • 只是认为您可以通过使用属性装饰枚举成员并将其用作字符串表示来解决 switch 语句周围的维护问题。不过,这将是一个大便。
    • 嘿托尼,我相信你建议的解决方案会很好用;虽然我不想在我的代码中添加硬编码的东西并让它有异味.. ;)
    【解决方案5】:

    =0 指定枚举值在基础类型 (int) 中的表示。您已经为 ENEN_CA 提供了相同的表示形式,即 0,因此现在它们实际上已成为相同值的两个标签。

    Enum.Parse 返回此值,它将等于两个标签。

    根本问题是您试图将分层概念编码为平面枚举。出于某些目的,所有讲英语的文化都应该被平等对待,而出于其他目的,您想将它们区分开来。

    我认为解决方案是使用already present mechanisms for localizing your application,特别是CultureInfo 应该替换您的枚举和Resources 您的查找字典。


    允许使用枚举让我有点惊讶 :) 但是MSDN documentation 明确声明它是有效的,并举例说明它如何有用:

    enum Color 
    {
       Red,
       Green,
       Blue,
       Max = Blue
    }
    

    【讨论】:

    • 允许两个名称具有相同的值是完全合理的,毕竟枚举只是人类可以理解的幻数表示。正如您所指出的,一旦编译器从值映射到幻数,值就会丢失。
    • @TonyHopkinson 只要您的代码从未在值上使用ToStringParse,那么就不存在明显 问题。一旦您进行字符串转换 - 例如在(反)序列化期间 - 您就会丢失信息。为方便起见使用别名很好,只要您知道问题可能由此产生。
    • @Corey 不,问题已经存在,因为 EN == EN_CA,无论字符串表示形式如何。枚举标签也是相等的。
    • @flup 我认为我的帖子和 cmets 很清楚我同意,拥有多个具有相同值的枚举成员是有问题的。但这样做有(相对)正当的理由。
    • @Corey 我就是这么说的,或者至少我以为我是这么说的。名称到值仅映射一种方式,因为一个值有两个名称。不仅仅是 Parser 和 ToString,LanguageType lt = LanguageType.EN_CA; if (lt = LanguageType.EN) ... 也不会像宣传的那样工作。
    【解决方案6】:

    这就是我解决问题的方法:

    我无法通过让 Enum.Parse 方法返回我所期望的结果来找到解决问题的方法。(请参阅注释*)

    我的解决方法是在结果字典上应用GroupBy(LanguageIndex),该字典的索引重复,因此引发异常。因为我希望 EN_CA 和 EN 在枚举中具有相同的值,这给了我我正在寻找的东西而不会引发异常。

    我可以看到我的修复不是对我最初提出的问题的实际答案,而忽略了上下文;尽管我认为它可能仍然适用于具有类似问题的其他上下文。

    note*:我本可以实现我自己的 Enum.Parse 版本 - 请参阅 this 答案以获得替代实现 - 但这需要我把臭味将东西硬编码到我的代码中,所以我放弃了修复 Parse 方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-26
      • 2018-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多