2010-09-21 2 views
4

Рассмотрит следующие два перечислений:Как определить представленный тип значения перечисления?

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

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

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

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

ответ

12

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

Например, если вы посмотрите на второй перечислении в рефлектор, вы увидите что-то вроде этого:

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

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

public enum Foo : long 
{ 
} 

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

+0

Это и заключение, к которому я пришел. Я понял, что C# рассматривал его как целое, независимо от того, но мне нужен был кто-то, чтобы подтвердить это для меня. Спасибо :) –

+0

@Nathan: Я отредактировал ответ, чтобы включить немного больше. В принципе, у вас не может быть базового типа char. –

1

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

8

Все перечисления должны использовать один из следующих типов в своем заявлении: 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())); 
    } 
} 

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

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

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

Это напечатает c на консоли.

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

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

UPDATE:

Вы можете объявить обугленного перечисление в 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()); 

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

error CS0570: 'Drawing.Color.Red' is not supported by the language

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

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"); 
    } 

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

+0

"(Возможно, метод Enum.GetUnderlyingType, похоже, тот, который вы искали, но он никогда не возвращает символ, потому что у вас не может быть перечисление символов на этом языке.)« Да, я тоже это испытал, это то, что привело я сделаю вывод, что char как тип значения действительно эквивалентное представление int32 символа. Все это было не для моих целей, а для того, чтобы подтвердить сделанные мной выводы, помогая кому-то другому. Спасибо за ваш вклад! –

+0

Рад, что я мог помочь. Что вы думаете о «расширении», которое я предложил? Может ли это помочь вам или вашему другу?Или это только создаст больше проблем? –

0

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

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'

+0

Это не говорит вам, что перечисление в соответствии с исходным кодом использует int или char. Он просто говорит вам, что enum * может * быть либо. Это опасный код. –

Смежные вопросы