可空枚举的扩展方法

15

我正在尝试编写可空枚举的扩展方法。
就像这个例子:

// ItemType is an enum
ItemType? item;
...

item.GetDescription();

所以我写了这个方法,但由于某些我不理解的原因,它无法编译:

public static string GetDescription(this Enum? theEnum)
{
    if (theEnum == null)
        return string.Empty;

    return GetDescriptionAttribute(theEnum);
}

我在Enum?上遇到了以下错误:

只有非可空值类型才能作为system.nullable的基础类型。

为什么?因为枚举类型不能有null值!

更新:

如果有很多枚举类型,ItemType只是其中一个示例。

4个回答

21

System.Enum是一个class,所以只需删除?,就可以正常工作。

(通过“正常工作”,我指的是如果您传递一个空值的ItemType?,则在方法中将得到一个nullEnum。)

public static string GetDescription(this Enum theEnum)
{
    if (theEnum == null)
        return string.Empty;
    return GetDescriptionAttribute(theEnum);
}
enum Test { blah }

Test? q = null;
q.GetDescription(); // => theEnum parameter is null
q = Test.blah;
q.GetDescription(); // => theEnum parameter is Test.blah

@gdoron 在我的情况下,Test?Nullable<Test> 都可以获得智能感知。 (VS2010) - Rawling
@gdoron 哈哈抱歉,我的意思是传递任何非可空枚举值都会在传递到方法之前将其装箱。 - Chris Sinclair
q 为空时,我在 q.GetDescription()q?.GetDescription() 中得到了不同的结果。前者是预期的,但后者返回了 default(Test) 的描述。有人能帮忙解释一下吗? - Scott
@Scott q.GetDescription() 调用 GetDescription(q) 即使 q 为空。q?.GetDescription() 如果 q 非空,则调用 GetDescription(q),但如果 q 为空,则返回 GetDescription 的返回类型的默认值。 - Rawling
显示剩余5条评论

4
您可以简单地将您的扩展方法变为通用泛型方法:
public static string GetDescription<T>(this T? theEnum) where T : struct
{ 
    if (!typeof(T).IsEnum)
        throw new Exception("Must be an enum.");

    if (theEnum == null) 
        return string.Empty; 
 
    return GetDescriptionAttribute(theEnum); 
}

很遗憾,您不能在泛型约束中使用System.Enum,因此扩展方法将显示所有可空值(因此需要额外检查)。

编辑:C# 7.3引入了新的泛型约束,现在允许将泛型参数限制为枚举类型,如下所示:

public static string GetDescription<T>(this T? theEnum) where T : Enum
{ 
    if (theEnum == null) 
        return string.Empty; 
 
    return GetDescriptionAttribute(theEnum); 
}

感谢@JeppeStigNielsen指出这一点。

2
这很好,因为它避免了装箱。您还可以说!theEnum.HasValue(如果您喜欢的话)。如果有人对泛型约束到System.Enum感兴趣,还可以阅读“枚举和委托的泛型约束”文章。 - Jeppe Stig Nielsen
2
@LenielMaccaferri 那篇2009年的博客文章现在可以在这里看到。上面的答案和我的评论是来自2012年的。然而,所有这些现在都已经过时了,因为自C# 7.3(从2018年开始)以来,您可以使用where T : struct, Enum,这将保证T是一个具有基类System.Enum的值类型。 - Jeppe Stig Nielsen

3

您应该在方法签名中使用实际的枚举类型:

public static string GetDescription(this ItemType? theEnum)

System.ValueTypeSystem.Enum不被视为值类型(只有从它们派生出的类型才是),因此它们是可空的(您不需要将它们指定为可空)。尝试一下:

// No errors!
ValueType v = null;
Enum e = null;

您可以尝试使用以下签名:

public static string GetDescription<T>(this T? theEnum) where T: struct

这也允许使用struct,但这可能不是你想要的。我记得有一些库可以在编译后添加enum类型约束(C#不允许)。只需要找到它... 编辑: 找到了: http://code.google.com/p/unconstrained-melody/

如果有很多枚举,ItemType 只是一个例子。我确信这是显而易见的,抱歉。 - gdoron
哦,你添加的第二部分回答了我的问题,谢谢。但是有没有办法克服这个奇怪的设计呢? - gdoron
你可以使用泛型约束,但是C#不允许将枚举指定为约束。请查看我的编辑以获得解决方案。 - Botz3000

0

或许更好的方法是为您的枚举添加额外的值并将其命名为 null :)


1
是的,我同意,一个枚举类型,其中一个值为“未定义”、“空”或其他类似的值会更有意义。或者,您可以将枚举类型包装在一个类中,并将其设置为 null,如果您真的需要一个 null 值的话。 - Mikey Mouse
2
什么?恰恰相反 - 只有值类型才能与Nullable <T>一起使用。 INullable又与此相关吗? - Allon Guralnek
枚举值 null 不是选项。不适用于 ASP.NET-MVC。 - gdoron

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