C# 枚举类型的 ToString() 方法是否需要装箱:解决或抑制?

3
最近我们在Visual Studio中安装了“Clr Heap Allocation Analyzer”扩展程序,以检查我们的代码中由装箱等原因引起的堆分配问题。我们遇到了“HAA0102”警告,该警告给出以下描述:
非覆盖值类型上的虚方法调用添加了Boxing或Constrained指令。
它是指以下代码行中的条件检查:
if(instanceType == Enums.Tags.DialoguePanel.ToString())
{
     // Some code handling
}
else if(instanceType == Enums.Tags.InfoPanel.ToString())
{
    // Some other code handling
}

为了提供背景信息,Enums.Tags.DialoguePanel 指的是以下枚举声明:

public enum Tags
        {
            InfoPanel,
            DialoguePanel,
            WarningPanel
        }

现在我明白enum是值类型,因此Enums.Tags.DialoguePanel也是值类型。我也知道.ToString()基本上是将其装箱到引用类型(字符串)。我的问题是,是否有必要重构这些行以避免装箱(如果需要,如何处理?),或者是否最好针对这些特定情况抑制此警告?请记住,我们确实需要保留枚举类型。它们被用作提供用户选项的受控方式(在下拉列表中)。重构这些枚举类型以成为另一种类型将需要大量工作(如果性能显着提高,则可以接受)。

1
问题应该是:“你为什么需要ToString?为什么不直接与实际的枚举值进行比较?” - MakePeaceGreatAgain
好的观点。我们还有一些情况需要将对象的字符串值与这些枚举.ToString()进行比较。例如,我们从客户的WebAPI中获取一个“object.name”,并且我们需要将其与我们的枚举(字符串)值进行比较。 - FerdieQO
3
如果你真的需要这些字符串 - 这似乎是一个值得怀疑的设计 - 那么请使用nameof(Enums.Tag.InfoPanel)而不是调用ToString。 - ckuri
这不是一个理想的设计,也是代码异味,我同意这一点。不幸的是,我们必须从客户的Web服务器获取数据。我们无法控制他们如何发送数据(以及以什么形式/类型发送)。 - FerdieQO
我赞同使用 nameof,这将把字符串编译为常量并嵌入到编译后的代码中,而不是每次调用 .ToString() 时都产生新的字符串。很可能这也会消除警告。 - Lasse V. Karlsen
2个回答

4

枚举类型相对于字符串类型具有一定优势,它添加了一个约束条件,即只有在定义的枚举中才能使用有效的值。显然,您的Web服务器接收字符串而不是枚举值,但正确的方法应该是将字符串强制转换为枚举值,然后进行枚举值的比较。

当然,将字符串值强制转换可能会出现问题,因此这应该被视为“验证步骤”。但是,一旦枚举值有效,您就不会遇到这种问题,这除了可能导致性能下降外,在我看来也容易出错(如果您期望值与枚举值之一匹配,但实际上没有匹配怎么办?)。

// "Validation"
instanceType = (Enums.Tags) Enum.Parse(typeof(Enums.Tags), value)

// Usage
if(instanceType == Enums.Tags.DialoguePanel)
{
     // Some code handling
}
else if(instanceType == Enums.Tags.InfoPanel)
{
    // Some other code handling
}

好的,很棒的建议,谢谢。是的,我们从 Web 服务器接收字符串。虽然我们可以尝试将字符串解析为枚举值,但性能下降显然是不可取的。所以如果我理解你的意思正确的话,最好保持现在的方式并抑制警告? - FerdieQO
你忘了进行枚举类型的转换而进行解析。 - ckuri
1
@FerdieQO 不,我的论点是消除首先检查字符串的需要。如果instanceType是一个枚举值,就不会有这个警告。 - Neil
好的,现在明白了。使用Enum.Parse将值转换为枚举值,然后将其与其他枚举值进行比较。这应该可以避免装箱(因此也避免警告),并防止它最终分配到堆上,对吗?顺便说一下,我很抱歉没有完全正确理解参数。英语不是我的母语。 - FerdieQO

3
现在我明白了 `enum` 是值类型,因此 `Enums.Tags.DialoguePanel` 也是值类型。我还理解到,调用 `.ToString()` 实际上就是将其装箱为引用类型(字符串)。但这不完全正确。装箱是因为 Enum.ToString 调用了 Enum.GetValue 方法,该方法返回基础值作为一个对象 - 因此需要装箱操作。而字符串是一个引用类型,所以它不是装箱的。并且仅仅因为字符串转换并不需要进行装箱。
另外,我们从 Web 服务器接收字符串。虽然我们可以尝试将字符串解析为枚举值,但性能下降肯定是不可取的。
我曾经创建过一个通用的Enum<TEnum>类,用于枚举操作而不需要装箱。虽然无装箱的Enum<TEnum>.ToString速度大约快了20倍(至少在this .NET Fiddle test中是这样),但我认为与Web服务器响应时间相比,由装箱引起的性能损失可以忽略不计,因此您可以忽略警告。但如果您愿意,可以试一试

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接