2015-09-05 2 views
2

Есть ли способ заставить перечислитель C# принять только одну из нескольких явно названных констант, или есть ли другая функция? Ссылка на C# имеет следующие соображения:C# enum type-safety

Можно присвоить произвольное целочисленное значение типу перечисления. Однако вы не должны этого делать, потому что неявное ожидание равно , что переменная enum будет удерживать только одно из значений, определяемых перечислением . Чтобы присвоить произвольное значение переменной перечисления , необходимо ввести высокий риск ошибок.

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

ответ

2

Способность бросить любое целое число на перечисление в основном из соображений производительности, но перечисление реализованы поскольку тип значения просто не может быть защищен от хранения неопределенных значений. Рассмотрим перечисление как:

public enum Condition { 
    Right = 1, 
    Wrong = 2 
} 

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

public class Demo { 
    public Condition Cond; 
} 

Когда ты создайте экземпляр класса, члены инициализируются до нуля, поэтому переменная-член Cond будет иметь неопределенное значение (Condition)0.

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

public sealed class SafeEnum<T> where T : struct { 

    public T Value { get; private set; } 

    public SafeEnum(T value) { 
    if (!(value is Enum)) { 
     throw new ArgumentException("The type is not an enumeration."); 
    } 
    if (!Enum.IsDefined(typeof(T), value)) { 
     throw new ArgumentException("The value is not defined in the enumeration."); 
    } 
    Value = value; 
    } 

} 

Пример:

var cond = new SafeEnum<Condition>(Condition.Right); // works 
Condition d = cond.Value; 

var cond = new SafeEnum<int>(42); // not an enum 

var cond = new SafeEnum<Condition>((Condition)42); // not defined 

Экземпляр класса может содержать только значение, определяется в перечислении, поскольку конструктор не позволит создать экземпляр иначе.

Поскольку класс неизменен, значение не может быть изменено на неопределенное значение.

3

Насколько я знаю, вы не можете остановить C#, позволяя конверсии между перечислениями и целыми числами.

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

Скажем у вас есть это перечисление:

enum DayOfWeek 
{ 
    Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday 
} 

Вы можете использовать запечатанный класс на своем месте. Преимущество состоит в том, что вы получаете бесплатные сравнения (потому что сравнение значений и сравнительное сравнение эквивалентны для этого случая). Недостатком является то, что (как и все ссылочные типы в C#), он имеет значение NULL.

sealed class DayOfWeek 
{ 
    public static readonly DayOfWeek Monday = new DayOfWeek(0); 
    public static readonly DayOfWeek Tuesday = new DayOfWeek(1); 
    public static readonly DayOfWeek Wednesday = new DayOfWeek(2); 
    public static readonly DayOfWeek Thursday = new DayOfWeek(3); 
    public static readonly DayOfWeek Friday = new DayOfWeek(4); 
    public static readonly DayOfWeek Saturday = new DayOfWeek(5); 
    public static readonly DayOfWeek Sunday = new DayOfWeek(6); 

    private readonly int _value; 

    private DayOfWeek(int value) 
    { 
     _value = value; 
    } 
} 

Или вы можете использовать структуру. Преимущество заключается в том, что он не может быть недействительным и, следовательно, еще более похож на перечисление.Недостаток заключается в том, что вы должны реализовать код сравнения вручную:

struct DayOfWeek 
{ 
    public static readonly DayOfWeek Monday = new DayOfWeek(0); 
    public static readonly DayOfWeek Tuesday = new DayOfWeek(1); 
    public static readonly DayOfWeek Wednesday = new DayOfWeek(2); 
    public static readonly DayOfWeek Thursday = new DayOfWeek(3); 
    public static readonly DayOfWeek Friday = new DayOfWeek(4); 
    public static readonly DayOfWeek Saturday = new DayOfWeek(5); 
    public static readonly DayOfWeek Sunday = new DayOfWeek(6); 

    private readonly int _value; 

    private DayOfWeek(int value) 
    { 
     _value = value; 
    } 

    public bool Equals(DayOfWeek other) 
    { 
     return _value == other._value; 
    } 

    public override bool Equals(object obj) 
    { 
     if (ReferenceEquals(null, obj)) 
     { 
      return false; 
     } 
     return obj is DayOfWeek && Equals((DayOfWeek)obj); 
    } 

    public override int GetHashCode() 
    { 
     return _value; 
    } 

    public static bool operator ==(DayOfWeek op1, DayOfWeek op2) 
    { 
     return op1.Equals(op2); 
    } 

    public static bool operator !=(DayOfWeek op1, DayOfWeek op2) 
    { 
     return !(op1 == op2); 
    } 
} 
+1

Существует одна вещь, которую он делает хуже - теперь вы можете назначить '' null' к DayOfWeek', потому что это класс. – MarcinJuraszek

+0

@MarcinJuraszek Вы правы. Я включил альтернативный подход. –

+1

Подход «struct» дает еще одну проблему: вы можете создать дополнительное значение, вызвав конструкцию безразличия. Компилятор определяет его по умолчанию для каждой структуры независимо от того, что. – MarcinJuraszek