82

我有一个看起来像这样的小方法:

public void SetOptions<T>() where T : Enum
{
    int i = 0;
    foreach (T obj in Enum.GetValues(typeof(T)))
    {
        if (i == 0)
            DefaultOption = new ListItem(obj.Description(), obj.ToString());
        i++;
        DropDownList.Items.Add(new ListItem(obj.Description(), obj.ToString()));
    }
}

基本上,我从一个枚举中填充一个下拉列表。Description()实际上是枚举的扩展方法,所以T绝对是enum.

但是,我想像obj将任何枚举转换为它的索引一样(int)obj,但是我收到一个错误,说我无法将 T 转换为 int。有没有办法做到这一点?

4

13 回答 13

66

您也可以将您的值转换为objectfirst 然后转换为int.

C# 7.3 及更高版本

使用Enum通用约束。

public static int EnumToInt<TValue>(this TValue value) where TValue : Enum
    => (int)(object)value;

C# 7.3 以下

没有Enum通用约束。

public static int EnumToInt<TValue>(this TValue value)  where TValue : struct, IConvertible
{
    if(!typeof(TValue).IsEnum)
    {
        throw new ArgumentException(nameof(value));
    }

    return (int)(object)value;
}

如果您的枚举继承自其他类型,例如从byte强制转换为 int 将抛出InvalidCastException.

您可以检查枚举的基本类型是否为整数。

public static int EnumToInt<TValue>(this TValue value) where TValue : Enum
{
    if (!typeof(int).IsAssignableFrom(Enum.GetUnderlyingType(typeof(TValue))))
        throw new ArgumentException(nameof(TValue));

    return (int)(object)value;
}

或者如果你使用Convert.ToInt32它,你将使用IConvertibleint32 的接口来转换不兼容的类型。

public static int EnumToInt<TValue>(this TValue value) where TValue : Enum
    => Convert.ToInt32(value);

请注意,转换uintint有符号/无符号对可能会导致意外行为。(装箱IConvertible和转换的性能不如拆箱。)


我建议为每个枚举基类型创建一个方法,以确保返回正确的结果。

于 2018-06-25T13:46:25.360 回答
57

尝试这个,

public void SetOptions<T>()
{
    Type genericType = typeof(T);
    if (genericType.IsEnum)
    {
        foreach (T obj in Enum.GetValues(genericType))
        {
            Enum test = Enum.Parse(typeof(T), obj.ToString()) as Enum;
            int x = Convert.ToInt32(test); // x is the integer value of enum
                        ..........
                        ..........
        }
    }
}
于 2013-06-06T11:45:15.180 回答
6

这是我针对 C# 7.3 及更高版本的解决方案。与 OP 的问题不完全匹配,但可能对从谷歌找到这个的人有用。与其他答案相比的主要优点是它返回一个 ulong,这意味着任何允许的枚举类型都适合它。我还对此机器代码和其他一些答案进行了比较。是的,我很无聊,并且想进行一些过早的优化。

private static unsafe ulong EnumAsUInt64<T>(T item) where T : unmanaged, Enum
{
    ulong x;
    if (sizeof(T) == 1)
        x = *(byte*)(&item);
    else if (sizeof(T) == 2)
        x = *(ushort*)(&item);
    else if (sizeof(T) == 4)
        x = *(uint*)(&item);
    else if (sizeof(T) == 8)
        x = *(ulong*)(&item);
    else
        throw new ArgumentException("Argument is not a usual enum type; it is not 1, 2, 4, or 8 bytes in length.");
    return x;
}
于 2020-04-03T03:08:00.470 回答
6

如果您的目标是 .NET Core,则可以Unsafe.As<TFrom, TTo>System.Runtime.CompilerServices命名空间中使用,如MSDN中所述。这里的优点是不会进行装箱,这是此处其他答案中唯一真正的性能成本。

private static int EnumToInt<TEnum>(TEnum enumValue) where TEnum : Enum
{
    return Unsafe.As<TEnum, int>(enumValue);
}

请注意,这种方法与其他现有答案一样存在安全问题:不能保证给定的枚举是兼容的int类型,这可能与此功能未内置的原因相同。如果您在内部使用这种方法,您可以确定传递给它的任何枚举都是兼容类型,那么这可能是最有效的方法。

是 dotnet 的 GitHub 页面上一个问题的链接,在该页面上提出了此问题,如果您想了解更多信息,一些开发人员详细说明了这种方法。

于 2020-02-02T00:13:40.050 回答
5

这个适用于任何基础类型

Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()))

例如,当您想向 中添加一个值时SqlCommand,它将枚举转换为,0并且您必须将其显式转换为匹配类型。但是我们可以编写以下扩展:

public static void AddEnum(this SqlParameterCollection parameters, string parameterName, Enum value)
{
    parameters.AddWithValue(parameterName, Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType())));
}

这是为我们做的一切。

于 2016-12-23T13:43:16.780 回答
4

你可以为此滥用 GetHashCode吗?

public enum MyEnum
{
  Foo = 100,
  Bar = 200,
  Fizz = 0
}

static void Main(string[] args)
{
  var i1 = MyEnum.Foo.GetHashCode();  // i1 = 100
  var i2 = MyEnum.Bar.GetHashCode();  // i2 = 200
  var i3 = MyEnum.Fizz.GetHashCode(); // i3 = 0
}

请注意:“设计GetHashCode()上只对一件事有用:将对象放入哈希表中。因此得名。” - E. Lippert

于 2015-03-24T10:45:40.927 回答
3

这是一个更简单的方法。

由于 Enum 实现了 IConvertible,我们可以使用 ToInt32(..)。

int? value = (enumCandidate as IConvertible)?.ToInt32(CultureInfo.InvariantCulture.Numberformat);

或者,如果您想要通用枚举的通用方法:

public static int GetEnumValue<T>(T inputEnum) where T: struct, IConvertible
{
    Type t = typeof(T);
    if (!t.IsEnum)
    {
        throw new ArgumentException("Input type must be an enum.");
    }

    return inputEnum.ToInt32(CultureInfo.InvariantCulture.NumberFormat);

}

或者更一般的:

public static int GetEnumValue(object enumInput)
{
    Type t = enumInput.GetType();
    if (!t.IsEnum)
    {
        throw new ArgumentException("Input type must be an enum.");
    }

    return ((IConvertible)inputEnum).ToInt32(CultureInfo.InvariantCulture.NumberFormat);

}
于 2018-02-21T23:47:28.833 回答
3

如果您将泛型 T 限制为 Enum 使用

where T: Enum

然后你可以使用下面的单线

public static int GetIndexFromEnum<T>(T enumValue) where T : Enum {
    int index = Convert.ToInt32(enumValue);
    return index;
}

这似乎是最简单的解决方案,只要您可以保证 T 将是一个枚举。

于 2020-02-13T19:12:53.303 回答
2

我很惊讶你的代码完全有效。Enum.GetValues返回一个整数数组 - 这是您要查找的值。而且,正如其他人所提到的,您不能将泛型限制为 enum

相反,您可能应该将您的Description方法称为常规静态方法,而不是扩展方法。

于 2013-06-06T11:11:52.743 回答
2

只需先将通用 T 转换为对象

T value;
int int_value = (int)(object)value;

而已。

于 2020-05-20T07:58:07.727 回答
1

使用 LINQ 这可以优雅地完成:

public static void SetOptions<T>(this DropDownList dropDownList)
{
    if (!typeof(T).IsEnum)
    {
        throw new ArgumentException("Type must be an enum type.");
    }

    dropDownList.Items.AddRange(Enum
        .GetValues(typeof(T))
        .Cast<Enum>()
        .Select(x => new ListItem(x.ToString(), Convert.ToInt32(x).ToString()))
        .ToArray());
}
于 2016-06-23T08:34:06.640 回答
0

试试这个:(假设 TEnum 有一个从 0 到 n 的编号)

public void SetOptions<TEnum>() where TEnum : Enum
{
    foreach (TEnum obj in Enum.GetValues(typeof(TEnum)))
    {
        var i = (int)(object)obj;
        if (i == 0) DefaultOption = new ListItem(obj.Description(), obj.ToString());
        DropDownList.Items.Add(new ListItem(obj.Description(), obj.ToString()));
    }
}
于 2019-09-02T14:25:19.863 回答
-3

要扩展 Jan 关于泛型和枚举值的答案:

void MyFunc<T>(T value)
{
    Type t = typeof(T);
    if(t.IsEnum)
    {
        int valueAsInt = value.GetHashCode(); // returns the integer value
    }
}
于 2015-05-17T13:18:01.160 回答