【问题标题】:Extension Method to Get the Values of Any Enum获取任意枚举值的扩展方法
【发布时间】:2014-05-21 22:09:12
【问题描述】:

我一直在尝试创建一个扩展方法,它适用于任何枚举,以返回其值。

不要这样做:

Enum.GetValues(typeof(BiasCode)).Cast<BiasCode>()

这样做会很好:

new BiasCode().Values()

如果没有 new 会更好,但这是另一个问题。

我有一个.NET fiddle,它的解决方案很接近(代码如下所示)。这段代码的问题是扩展方法返回List&lt;int&gt;。我想让它返回枚举值本身的列表。返回List&lt;int&gt; 并不可怕;这只是意味着我必须转换结果。

甚至有可能做到这一点吗?我尝试使扩展方法通用,但遇到了问题。这是我所能得到的最接近的:

using System;
using System.Linq;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        foreach (int biasCode in new BiasCode().Values())
        {
            DisplayEnum((BiasCode)biasCode);
        }
    }

    public static void DisplayEnum(BiasCode biasCode)
    {
        Console.WriteLine(biasCode);    
    }
}

public enum BiasCode
{
    Unknown,
    OC,
    MPP
}

public static class EnumExtensions
{
    public static List<int> Values(this Enum theEnum)
    {
        var enumValues = new List<int>();
        foreach (int enumValue in Enum.GetValues(theEnum.GetType()))
        {
            enumValues.Add(enumValue);
        }

        return enumValues;
    }
}

【问题讨论】:

  • 在与 Jon Skeet 就这个问题进行了单独讨论后,我接受了甚至不是扩展方法的答案。似乎是正确的做法。与乔恩讨论:stackoverflow.com/a/23808495/279516

标签: c# enums extension-methods


【解决方案1】:

您可以返回适当枚举类型的实例(使用反射创建),但其静态类型不能是List&lt;EnumType&gt;。这将要求 EnumType 成为该方法的泛型类型参数,但该类型必须仅限于枚举类型和 that is not possible in C#

但是,您可以在实践中足够接近(并添加运行时检查),因此您可以编写一个像这样工作的方法:

public static IEnumerable<TEnum> Values<TEnum>()
where TEnum : struct,  IComparable, IFormattable, IConvertible
{
    var enumType = typeof(TEnum);

    // Optional runtime check for completeness    
    if(!enumType.IsEnum)
    {
        throw new ArgumentException();
    }

    return Enum.GetValues(enumType).Cast<TEnum>();
}

你可以调用它

var values = Values<BiasCode>();

我已经让方法返回 IEnumerable&lt;TEnum&gt; 而不是一个额外的 LINQ-y 风格的列表,但是您可以轻松地返回一个带有 .ToList() 的真实列表。

【讨论】:

  • IComparable、IFormattable 和 IConvertible 是否用作类型约束,因为这些是枚举实现的接口?
  • @BobHorn:是的。它们是为了“尽可能接近”适当的enum 约束。
  • 我刚刚为这篇文章添加了一个新答案。你能告诉我这不是解决这个问题的好方法吗?
  • @BobHorn: a) 你没有得到足够强的类型——除了打印它们之外,你打算如何处理这些值?给定 List&lt;Enum&gt; 而没有其他信息,很难用它做任何有意义的事情,并且 b) 所需的语法 new Foo.Values()Foo.SomeValue.Values() 充其量是不幸的。
  • 我可以帮助您理解您的意思。我不确定我失去了什么。正如我刚刚回复 Edin 一样,小提琴表明调用者不必再次投射。
【解决方案2】:

你可以这样声明你的方法:

public static List<T> Values<T>() where T : struct
{
    var type = typeof(T);

    if(!type.IsEnum) return null; // or throw exception

    return Enum.GetValues(type).Cast<T>().ToList();
}

那你就可以调用它了

Values<BiasCode>();

【讨论】:

  • 既然这不是一个扩展方法,那么它会放在某个静态实用程序类中吗?然后调用将类似于:EnumUtility.Values&lt;BiasCode&gt;();
  • @BobHorn 是的,如果您将此方法放入一个类或静态扩展类中,您还应该指定该类的名称。
  • 我刚刚为这篇文章添加了一个新答案。你能告诉我这不是解决这个问题的好方法吗?
【解决方案3】:

我想知道我是否遗漏了什么,因为所有答案都使用通用方法作为解决方案的一部分。为什么不做这样的事情呢?

public static List<Enum> Values(this Enum theEnum)
{
    return Enum.GetValues(theEnum.GetType()).Cast<Enum>().ToList();
}

小提琴在这里:https://dotnetfiddle.net/FRDuvD

这样这个扩展方法将只对枚举可用。使用泛型方法,扩展方法似乎适用于所有类型:

string someString = "x";
someString.Values();

在编译时最好不要让 Values() 可用于字符串。

【讨论】:

  • 我想我们都同意没有最好的解决方案,因为不可能对 Enum 类型进行 where 子句约束。您使用此解决方案做得更好的是仅对 Enum 值的约束。但是,与我的解决方案相比,您丢失了有关您正在调用扩展方法的枚举类型的信息。所以你需要再次施放它。因此,我认为这与最初在您的问题中发布的方法之间没有太大区别,您返回 List 并需要对其进行转换。
  • 关于这个:“......你失去了关于枚举类型的信息......”这个:“所以你需要再次投射它。”你看到小提琴了吗?调用者不需要再次投射。调用者可以将枚举传递给 DisplayEnum() 方法而无需强制转换。此外,DisplayEnum() 方法可以使用特定的枚举作为其参数,而不仅仅是接受 Enum。
【解决方案4】:

这个怎么样

class Program{
    static void Main(string[] args)
    {
        BiasCode b = BiasCode.MPP;
        var these = b.Values().ToList();
        //...  these contains the actual values as the enum type
    }
}
public static class EnumExtensions
{
    public static IEnumerable<T> Values<T>(this T theEnum) where T : struct,IComparable, IFormattable, IConvertible
    {
        var enumValues = new List<T>();

        if ( !(theEnum is Enum))
            throw new ArgumentException("must me an enum");

        return Enum.GetValues(typeof(T)).Cast<T>();
    }
}

【讨论】:

  • 我刚刚为这篇文章添加了一个新答案。你能告诉我这不是解决这个问题的好方法吗?
【解决方案5】:

由于您正在寻找扩展方法,所以它是:

public static class EnumExtensions
{
   public static List<T> Values<T>(this T theEnum)
       where T : struct,  IComparable, IFormattable, IConvertible
   {
       if (!typeof(T).IsEnum) 
           throw new InvalidOperationException(string.Format("Type {0} is not enum.", typeof(T).FullName)); 

       return Enum.GetValues(theEnum.GetType()).Cast<T>().ToList();
   }
}

编辑

我将在此处将我的评论合并到 Bob 的回答中:

所以,我想我们都同意没有最好的解决方案,因为不可能对 Enum 类型进行 where 子句约束。另一种解决方案是使用以下方法签名(如 Bob 建议的那样):

public static List<Enum> Values(this Enum theEnum)

用这个解决方案我们会做得更好的是仅对 Enum 值的约束。但是,与通用解决方案相比,我们丢失了有关枚举类型的信息,我们正在调用您的扩展方法。所以我们需要再次施放它。而且我认为这与 Bob 在他的问题中最初发布的方法之间没有太大区别,他返回 List&lt;int&gt; 并需要将其转换回我们的枚举。

使用带有静态检查的Code Contracts 可以实现最优雅的解决方案。但是,它需要使用代码合约,如果要在这种情况下使用它们可能会有点矫枉过正。这种方法在以下线程中得到解决:Using code contracts to make a generic to be of type enum

【讨论】:

  • 你怎么称呼这个?您是否必须仅使用其中一个元素来创建枚举的实例?
  • 是的。由于是扩展方法,所以需要一个实例:List values = SomeEnumType.A.Values();
  • 我为此创建了一个小提琴:dotnetfiddle.net/E3wb1w。看起来不必选择枚举值,我也可以这样做:new BiasCode().Values()。我喜欢这样,因为它不会让我感到困惑。
  • 你能解释一下如何在枚举上识别扩展方法吗?如果调用是这样的:new BiasCode().Values(),既然扩展方法是泛型的,那么编译器怎么知道这个扩展方法适用于我的枚举呢? (特别是因为在调用代码中没有指定泛型类型参数。)
  • 这是我的猜测:由于扩展方法是针对泛型类型的,因此无论何时调用 Values(),所使用的类型都是在调用 Values() 的任何对象上。这意味着此扩展方法可用于所有对象,除非它在编译时也受到扩展方法签名中指定的约束的约束。
【解决方案6】:

获取C#中任意枚举值的扩展方法

基于上述解决方案,方法略有不同:

public static class Enum<TEnum> where TEnum : struct, IComparable, IFormattable, IConvertible
{
    public static IEnumerable<TEnum> GetAll()
    {
        var t = typeof(TEnum);
        if (!t.IsEnum)
            throw new ArgumentException();

        return Enum.GetValues(t).Cast<TEnum>();
    }
}

用法

var values = Enum<MyEnum>.GetAll();

测试

public enum Test
{
    First,
    Second,
    Third
}

[TestClass]
public class EnumTests
{
    [TestMethod]
    public void MyTestMethod()
    {
        var values = Enum<Test>.GetAll();
        Assert.AreEqual(3, values.Count());
    }
}

【讨论】:

  • 很好的使用和测试,但请注意这不是扩展方法。
猜你喜欢
  • 2010-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-13
  • 1970-01-01
  • 2010-12-10
  • 1970-01-01
相关资源
最近更新 更多