Как определить представленный тип значения enum? - PullRequest
4 голосов
/ 21 сентября 2010

Рассмотрим следующие два перечисления:

enum MyEnum1 {
    Value1 = 1,
    Value2 = 2,
    Value3 = 3
}

enum MyEnum2 {
     Value1 = 'a',
     Value2 = 'b',
     Value3 = 'c'
}

Я могу получить физическое значение, представленное этими значениями перечисления, с помощью явного приведения, ((int)MyEnum1.Value2) == 2 или ((char)MyEnum2.Value2) == 'b', но что, если я хочу получить представление char или представление int, не зная заранее, к какому типу приводиться?

Возможно ли получить базовое значение перечисления без приведения или, по крайней мере, программно возможно определить правильный тип базового значения?

Ответы [ 4 ]

12 голосов
/ 21 сентября 2010

Базовое значение обоих перечислений равно int.Вы просто используете тот факт, что существует неявное преобразование из char в int во втором случае.

Например, если вы посмотрите на второе перечисление в Reflector, вы увидите что-то вродеthis:

internal enum MyEnum2
{
    Value1 = 0x61,
    Value2 = 0x62,
    Value3 = 0x63
}

EDIT: если вам нужен другой базовый тип, вы должны указать его, например,

public enum Foo : long
{
}

Однако, только byte, sbyteshort, ushort, int, uint, long и ulong являются допустимыми базовыми типами перечислений.

8 голосов
/ 21 сентября 2010

Все перечисления должны использовать в своем объявлении один из следующих типов: byte, sbyte, short, ushort, int, uint, long или ulong.Вот как вы указываете тип:

enum MyEnum3 : long {
  Value1 = 5L,
  Value2 = 9L,
  Value3 = long.MaxValue
}

Если вы не укажете тип, по умолчанию будет int.

К сожалению, вы не можете указать char какбазовый тип.Вы можете создать это «расширение» в качестве пользовательского атрибута:

[AttributeUsage(AttributeTargets.Enum)]
public class CharAttribute : Attribute { }

[Char] enum MyEnum2 {
  Value1 = 'a',
  Value2 = 'b',
  Value3 = 'c'
}

И затем иметь такой класс:

public static class EnumEx {
  public static Type GetUnderlyingType(Type enumType) {
    if (!enumType.IsEnum) throw new ArgumentException();
    if (enumType.GetCustomAttributes(typeof(CharAttribute), false).Length > 0) {
      return typeof(char);
    }
    return Enum.GetUnderlyingType(enumType);
  }
  public static object ConvertToUnderlyingType(object enumValue) {
    return Convert.ChangeType(enumValue,
      GetUnderlyingType(enumValue.GetType()));
  }
}

(Кстати, метод Enum.GetUnderlyingType, кажется, тот, который вы искали, но он никогда не возвращает char, потому что у вас не может быть перечислений в языке.)

Это позволит вам получить более широкое представление о символеenums:

var value3 = EnumEx.ConvertToUnderlyingType(MyEnum2.Value3);
Console.WriteLine(value3);

Это выведет c на консоль.

Обратите внимание на базовый тип и значения ваших символов, они должны идеально вписываться в charчтобы избежать сбоев преобразования (переполнения).Безопасные типы имеют ширину 16 бит (как char) или меньше: byte, sbyte, short или ushort.Другие типы в порядке, если значения в перечислении могут быть преобразованы в 16-битные символы без потери точности (как в примере выше).

Использование стандартного (int) базового типа и литералов в качестве значений перечисления (которые неявно преобразуются в int) достаточно.

ОБНОВЛЕНИЕ:

Вы можете объявить перечисление в F #:

namespace Drawing

type Color =
   | Red = 'r'
   | Green = 'g'
   | Blue = 'b'

В C # вы можете использовать его следующим образом:

Console.WriteLine(Enum.GetUnderlyingType(typeof(Color)));

И он напечатает System.Char.

Но ... C # будет жаловаться, если вы попытаетесь использовать его значения.Это:

Console.WriteLine(Color.Red.ToString());

Выдает ошибку компилятора:

Ошибка CS0570: «Drawing.Color.Red» не поддерживается языком

В VB.NET нет ошибки компиляции, но есть ошибка времени выполнения от Enum.GetName.Похоже, что среда выполнения не готова работать с перечислениями.Вот выдержка из этого метода (из Reflector):

if (((!type.IsEnum && (type != intType)) && ((type != typeof(short)) && (type != typeof(ushort)))) && (((type != typeof(byte)) && (type != typeof(sbyte))) && (((type != typeof(uint)) && (type != typeof(long))) && (type != typeof(ulong)))))
  {
    throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnumBaseTypeOrEnum"), "value");
  }

Он проверяет не только то, что тип является перечислением, но и то, что он также является одним из вышеупомянутых базовых типов (для которых символ не являетсяопция). Так что вы действительно не должны создавать перечисления в F #. Вы можете использовать подход «расширения», который я описал.

1 голос
/ 21 сентября 2010

И enum - это просто тип int за кадром. Вы можете изменить его на long, byte и некоторые другие, но они должны быть числами. Char просто работает здесь, потому что его можно просто заменить на int. Так что, извините, я не думаю, что это возможно.

0 голосов
/ 25 марта 2012

Попробуйте, это работает для меня:

public static bool IsCharEnum(this Type T)
{
    var enumType = T.IsNullableEnum() ? Nullable.GetUnderlyingType(T) : T;
    if (!enumType.IsEnum) return false;
    try
    {
        return ((int[])Enum.GetValues(enumType)).All(i => char.IsLetter((char)i));
    }
    catch
    {
        return false;
    }
}

public static bool IsNullableEnum(this Type T)
{
    var u = Nullable.GetUnderlyingType(T);
    return (u != null) && u.IsEnum;
}

Добавьте этот код в класс расширений, и вы сможете использовать его как:

if (propValue.GetType().IsCharEnum())
{
    propValue = ((char)Convert.ToInt32(propValue)).ToString();
}

Когда propValue имеет тип MyEnum2,значение будет изменено на символ и будет 'a', 'b' или 'c'

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...