如何向Enums添加扩展方法


122

我有这个枚举代码:

enum Duration { Day, Week, Month };

我可以为此枚举添加扩展方法吗?




简短的回答,是的。在这种情况下,您可能需要考虑使用TimeSpan
Jodrell

1
在枚举上使用扩展方法会使我感到肮脏。创建一个类以封装所需的内容。保持枚举尽可能简单。如果您需要与之关联的更多逻辑,则创建一个Duration类,该类公开日,周,月以及包含扩展方法中可能包含的任何其他逻辑。
杰森·埃文斯

2
我喜欢为标志组使用枚举扩展方法。我喜欢在举例来说如果条款Day.IsWorkday()(Day & Days.Workday) > 0Days.Workday定义Monday | Tuesday ... | Friday。在我看来,前者更为清晰,而后者已完全实现。
塞巴斯蒂安·韦克'18

Answers:


114

根据这个网站

扩展方法提供了一种以现有方法编写现有类的方法,以您团队中其他人可能真正发现和使用的方式。鉴于枚举是和其他类别一样的,可以扩展它们也就不足为奇了,例如:

enum Duration { Day, Week, Month };

static class DurationExtensions 
{
  public static DateTime From(this Duration duration, DateTime dateTime) 
  {
    switch (duration) 
    {
      case Day:   return dateTime.AddDays(1);
      case Week:  return dateTime.AddDays(7);
      case Month: return dateTime.AddMonths(1);
      default:    throw new ArgumentOutOfRangeException("duration");
    }
  }
}

我认为枚举通常不是最佳选择,但至少这可以使您集中一些switch / if处理并将其抽象一些,直到可以做得更好。切记还要检查值是否也在范围内。

您可以在Microsft MSDN上了解更多信息


我认为“枚举是邪恶的”评论不合时宜,但有现实依据。我确实发现枚举可能是一个过度使用的问题,因为它们将您锁定在某些上下文和行为中。
Ed Schwehm 2013年

1
C#枚举很烂,因为它可以编译并运行:Duration d = 0;
Graham

16
Given that enums are classes不,他们不是课程。
温格·森顿

1
仅当它直接与枚举有关时,才使用这种扩展,例如一周中的几天以及扩展方法IsWeekday(),IsWeekendday()。但是类是用来封装行为的,因此,如果要封装很多或复杂的行为,则一个类可能会更好。如果它是有限且基本的,我个人会发现枚举可以扩展。与大多数设计决策一样,选择之间也存在模糊的界限(IMO)。值得注意的是,扩展只能在顶级静态类上进行,而不能在嵌套类上进行。如果您的枚举属于课程的一部分,则需要进行课程。
FreeText

51

您还可以将扩展方法添加到Enum类型,而不是Enum的实例:

/// <summary> Enum Extension Methods </summary>
/// <typeparam name="T"> type of Enum </typeparam>
public class Enum<T> where T : struct, IConvertible
{
    public static int Count
    {
        get
        {
            if (!typeof(T).IsEnum)
                throw new ArgumentException("T must be an enumerated type");

            return Enum.GetNames(typeof(T)).Length;
        }
    }
}

您可以通过执行以下操作来调用上述扩展方法:

var result = Enum<Duration>.Count;

这不是真正的扩展方法。它仅适用于Enum <>与System.Enum不同的类型。


该类可以static确保其所有方法的行为都类似于扩展吗?
贾廷·桑格维

15
对于将来的读者:名称的含糊性Enum<T>有点令人困惑。也可以调用该类EnumUtils<T>,方法调用将解析为EnumUtils<Duration>.Count
Namoshek

46

当然,您可以说,例如,要DescriptionAttribue在您的enum值上使用:

using System.ComponentModel.DataAnnotations;

public enum Duration 
{ 
    [Description("Eight hours")]
    Day,

    [Description("Five days")]
    Week,

    [Description("Twenty-one days")] 
    Month 
}

现在,您希望能够执行以下操作:

Duration duration = Duration.Week;
var description = duration.GetDescription(); // will return "Five days"

您的扩展方法GetDescription()可以编写如下:

using System.ComponentModel;
using System.Reflection;

public static string GetDescription(this Enum value)
{
    FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
    if (fieldInfo == null) return null;
    var attribute = (DescriptionAttribute)fieldInfo.GetCustomAttribute(typeof(DescriptionAttribute));
    return attribute.Description;
}

除了我使用DisplayAttribute Localized GetDescription之外,我一直在寻求创建几乎与您的示例完全一样的扩展。欢呼声
乔治

这是一个不错的选择,尽管我认为名称空间只是System.ComponentModel?
TomDestry

好的视角,感谢您展示实现以及扩展代码。继续:在您的实现中,您也可以像这样调用它:var description = Duration.Week.GetDescription();
斯宾塞沙利文

31

所有答案都很好,但他们都在谈论将扩展方法添加到特定类型的枚举中。

如果要向所有枚举添加方法,例如返回当前值的int而不是显式强制转换,该怎么办?

public static class EnumExtensions
{
    public static int ToInt<T>(this T soure) where T : IConvertible//enum
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException("T must be an enumerated type");

        return (int) (IConvertible) soure;
    }

    //ShawnFeatherly funtion (above answer) but as extention method
    public static int Count<T>(this T soure) where T : IConvertible//enum
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException("T must be an enumerated type");

        return Enum.GetNames(typeof(T)).Length;
    }
}

背后的窍门IConvertible是其继承层次结构,请参见MDSN

感谢ShawnFeatherly的回答


1
最好的答案!
Marco Alves

同上。如果我直接调用该扩展名(例如MyExtention.DoThing(myvalue)),但实际上未附加到枚举(例如myvalue.DoThing()
正常工作-Sinaesthetic

1
仅供参考,C#7.3现在支持将Enum作为通用类型约束
redtetrahedron

7

您可以为任何内容创建扩展,甚至 object(尽管这不是最佳做法)。就像方法一样理解扩展public static方法。您可以在方法上使用任何喜欢的参数类型。

public static class DurationExtensions
{
  public static int CalculateDistanceBetween(this Duration first, Duration last)
  {
    //Do something here
  }
}

5

参见MSDN

public static class Extensions
{
  public static string SomeMethod(this Duration enumValue)
  {
    //Do something here
    return enumValue.ToString("D"); 
  }
}

7
void对枚举返回值是一种奇怪的。我会考虑一个更现实的示例。
psubsee2003

3
@ psubsee2003 OP当然有足够的知识来更改此设置以满足他的需求?为什么样本很重要,足以回答最初的问题。
LukeHennerley 2013年

3
我是唯一在MSDN上发现代码示例很奇怪的人吗?大多数情况下,您需要付出一些真正的努力才能了解他们正在尝试做什么!
堆叠

0

我们刚刚为C#作了一个枚举扩展https://github.com/simonmau/enum_ext

它只是typesafeenum的一种实现,但是效果很好,所以我们制作了一个共享包-乐在其中

public sealed class Weekday : TypeSafeNameEnum<Weekday, int>
{
    public static readonly Weekday Monday = new Weekday(1, "--Monday--");
    public static readonly Weekday Tuesday = new Weekday(2, "--Tuesday--");
    public static readonly Weekday Wednesday = new Weekday(3, "--Wednesday--");
    ....

    private Weekday(int id, string name) : base(id, name)
    {
    }

    public string AppendName(string input)
    {
        return $"{Name} {input}";
    }
}

我知道这个例子是没有用的,但是你明白了;)


0

一个简单的解决方法。

public static class EnumExtensions
{
    public static int ToInt(this Enum payLoad) {

        return ( int ) ( IConvertible ) payLoad;

    }
}

int num = YourEnum.AItem.ToInt();
Console.WriteLine("num : ", num);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.