枚举的扩展方法,而不是枚举实例

49

我有一个类似下面的枚举来表示我的“物品”:

public enum Things
{
   OneThing,
   AnotherThing
}

我想要为这个枚举编写一个扩展方法(类似于Prise在这里的答案),但是该方法仅适用于枚举实例,如下:

Things thing; var list = thing.ToSelectList();

我希望它能够在实际枚举上运行:
var list = Things.ToSelectList();

我只需这样做

var list = default(Things).ToSelectList();

但是我不喜欢那个样子 :)
使用以下扩展方法,我已经更接近了:
public static SelectList ToSelectList(this Type type)
{
   if (type.IsEnum)
   {
      var values = from Enum e in Enum.GetValues(type)
                   select new { ID = e, Name = e.ToString() };
      return new SelectList(values, "Id", "Name");
   }
   else
   {
      return null;
   }
}

如下使用:

var list = typeof(Things).ToSelectList();

我们能做得比那更好吗?


你可以创建一个伪装成枚举的类。我猜缺点主要是非标准编程和无法摆脱从Object继承的方法。 - Iucounu
我不认为将动词用于类名是正确的做法。理想情况下,类名应该是名词,并且本身具有意义。 - user3264784
7个回答

76

扩展方法只能在实例上使用,因此无法完成此操作,但是通过一些选择合适的类/方法名称和泛型,您可以产生看起来同样好的结果:

public class SelectList
{
    // Normal SelectList properties/methods go here

    public static SelectList Of<T>()
    {
        Type t = typeof(T);
        if (t.IsEnum)
        {
            var values = from Enum e in Enum.GetValues(type)
                         select new { ID = e, Name = e.ToString() };
            return new SelectList(values, "Id", "Name");
        }
        return null;
    }
}

然后你可以像这样获取你的选择列表:
var list = SelectList.Of<Things>();

我认为这句话比 Things.ToSelectList() 更易读。


3
当我读到这段话时,觉得太棒了,甚至笑出了声。我一直专注于将 enum 转换为 selectlist,从没想过反过来做。 - spaceman
你可以创建一个伪装成枚举的类。我想缺点主要是非标准编程和无法摆脱从Object继承的方法。 - Iucounu
3
@Iucounu说道:缺点是它不是一个枚举。您将无法获得范围检查、数字表示、简单的数据库存储、自动字符串转换或任何其他枚举的优点。考虑到这些缺点并且没有真正的优点,我想不出您为什么要这样做。 - Aaronaught
4
我完全理解了,这是Java早期的“类型安全枚举”模式,在语言设计师最终实现实际枚举之前使用。你的解决方案不是一种解决方案,因为问题涉及实际枚举,并且除此之外,对其他答案的评论并不是宣传自己的适当媒介。 - Aaronaught
明白了,谢谢。这看起来很不错,我真的很期待使用它。现在我遇到了“'SelectList'不包含接受3个参数的构造函数”的问题。你有喜欢使用的构造函数吗? - InteXX
显示剩余5条评论

5

最好的做法是将其放在静态类中,像这样:

public static class ThingsUtils { 
    public static SelectList ToSelectList() { ... }
}

5

Aaronaught的回答非常好,基于此我做了以下实现:

public class SelectList
{
    public static IEnumerable<Enum> Of<T>() where T : struct, IConvertible
    {
        Type t = typeof(T);
        if (t.IsEnum)
        {
            return Enum.GetValues(t).Cast<Enum>();
        }
        throw new ArgumentException("<T> must be an enumerated type.");
    }
}

在我看来,这种方法更安全一些,因为你几乎只能使用枚举调用它。当然,如果你想要一个不会抛出异常的版本,你可以简单地返回 null 而不是抛出异常。


我认为你想要将其转换为T,而不是枚举,并返回一个IEnumerable<T>。 - maembe

1

@Aaronaught的回答非常好。为了扩展他的回答,你甚至可以让它更加通用化。我在一个全局库中有这个...

public static IQueryable GetAllEnumValues<T>()
{
    IQueryable retVal = null;

    Type targetType = typeof(T);
    if(targetType.IsEnum)
    {
        retVal = Enum.GetValues(targetType).AsQueryable();
    }

    return retVal;
}

现在你已经将这个功能从SelectList类中解耦出来了。因此,你可以在SelectList方法中调用它,或者在任何其他地方调用它。

public class SelectList
{
    public static SelectList Of<T>
    {
        IQueryable enumValues = GetAllEnumValues<T>();
        var values = 
            from Enum e in enumValues
            select new { ID = e, Name = e.ToString() };
        return new SelectList(values, "Id", "Name");
    }
}

另外需要注意的是,您可以使用 where 子句来改进枚举类型的通用方法编译... GetAllEnumValues<T>() where T : struct, IConvertible - undeniablyrob

1

我使用'Type'而不是'Enum'来添加扩展。这样我就可以从该方法中获取任何类型的列表。在这里它返回字符串值:

    public static string[] AllDescription(this Type enumType)
    {
        if (!enumType.IsEnum) return null;

        var list = new List<string>();
        var values = Enum.GetValues(enumType);

        foreach (var item in values)
        {
            // add any combination of information to list here:
            list.Add(string.Format("{0}", item));

            //this one gets the values from the [Description] Attribute that I usually use to fill drop downs
            //list.Add(((Enum) item).GetDescription());
        }

        return list.ToArray();
    }

稍后我可以使用这个语法来获取我想要的内容:
var listOfThings = typeof (Things).AllDescription();

0

在我看来,这是最干净的方法。为什么?

  • 它适用于任何 System.Enum
  • 扩展方法本身更加简洁。
  • 要调用它,只需添加 new,这是一个小的折衷(因为它必须有一个实例才能工作)。
  • 你不会传递 null,如果你尝试将其与另一种类型一起使用,它就不会编译。

用法:

(new Things()).ToSelectList()

扩展方法:
[Extension()]
public SelectList ToSelectList(System.Enum source)
{
    var values = from Enum e in Enum.GetValues(source.GetType)
                select new { ID = e, Name = e.ToString() };
    return new SelectList(values, "Id", "Name");    
}

-1
我认为你能够做的最接近的方式是稍微模拟枚举的功能。这是我想到的方法,虽然只是要在一个枚举上放置一个静态方法,但似乎需要做很多工作,不过我确实理解其编程吸引力。
    public class PseudoEnum
{
    public const int FirstValue = 1;
    private static PseudoEnum FirstValueObject = new PseudoEnum(1);

    public const int SecondValue = 2;
    private static PseudoEnum SecondValueObject = new PseudoEnum(2);

    private int intValue;

    // This prevents instantation; note that we cannot mark the class static
    private PseudoEnum() {}

    private PseudoEnum(int _intValue)
    {
        intValue = _intValue;
    }

    public static implicit operator int(PseudoEnum i)
    {
        return i.intValue;
    }

    public static implicit operator PseudoEnum(int i)
    {
        switch (i)
        {
            case FirstValue :
                return FirstValueObject;
            case SecondValue :
                return SecondValueObject;
            default:
                throw new InvalidCastException();
        }
    }

    public static void DoSomething(PseudoEnum pe)
    {
        switch (pe)
        {
            case PseudoEnum.FirstValue:
                break;
            case PseudoEnum.SecondValue:
                break;
        }
    }

}

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