Могу ли я избежать приведения значения enum при попытке использовать или вернуть его? - PullRequest
28 голосов
/ 23 февраля 2009

Если у меня есть следующее перечисление:

public enum ReturnValue{
    Success = 0,
    FailReason1 = 1,
    FailReason2 = 2
    //Etc...
}

Могу ли я избежать каста по возвращении, например:

public static int main(string[] args){
    return (int)ReturnValue.Success;
}

Если нет, почему значение enum по умолчанию не рассматривается как int?

Ответы [ 8 ]

49 голосов
/ 23 февраля 2009

перечисления должны быть типобезопасными. Я думаю, что они не сделали их неявно кастируемыми, чтобы препятствовать другим применениям. Хотя структура позволяет вам присвоить им постоянное значение, вам следует пересмотреть свои намерения. Если вы в основном используете перечисление для хранения постоянных значений, рассмотрите возможность использования статического класса:

public static class ReturnValue
{
    public const int Success = 0;
    public const int FailReason1 = 1;
    public const int FailReason2 = 2;
    //Etc...
}

Это позволяет вам сделать это.

public static int main(string[] args){
    return ReturnValue.Success;
}

EDIT

Когда вы делаете хотите предоставить значения для перечисления, это когда вы хотите объединить их. Смотрите пример ниже:

[Flags] // indicates bitwise operations occur on this enum
public enum DaysOfWeek : byte // byte type to limit size
{
    Sunday = 1,
    Monday = 2,
    Tuesday = 4,
    Wednesday = 8,
    Thursday = 16,
    Friday = 32,
    Saturday = 64,
    Weekend = Sunday | Saturday,
    Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday
}

Это перечисление можно затем использовать с помощью побитовой математики. См. Пример ниже для некоторых приложений.

public static class DaysOfWeekEvaluator
{
    public static bool IsWeekends(DaysOfWeek days)
    {
        return (days & DaysOfWeek.Weekend) == DaysOfWeek.Weekend;
    }

    public static bool IsAllWeekdays(DaysOfWeek days)
    {
        return (days & DaysOfWeek.Weekdays) == DaysOfWeek.Weekdays;
    }

    public static bool HasWeekdays(DaysOfWeek days)
    {
        return ((int) (days & DaysOfWeek.Weekdays)) > 0;
    }

    public static bool HasWeekendDays(DaysOfWeek days)
    {
        return ((int) (days & DaysOfWeek.Weekend)) > 0;
    }
}
3 голосов
/ 10 апреля 2009

Перечисление c # бесполезно.

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

  • Предоставьте неявный оператор для преобразования из вашего типа в общий тип int, чтобы вам не приходилось приводить.
  • Предоставьте явный оператор для преобразования из int в ваш тип, который выдает ошибку, если целое число не удовлетворяет ограничению, например (int x) => (x> = 0 && x <= 2). </li>

При использовании этого метода создайте общий неизменный базовый класс, такой как ConstrainedNumber<T>, у которого есть конструктор, который принимает значение T и делегирует ограничение: delegate bool NumberConstraint<T>(T value). Конструктор должен выполнить значение через делегат ограничения и выдать исключение, если оно не соответствует ограничению. Базовый класс должен также заботиться о неявной операции преобразования в T и должен обрабатывать равенство, перегружая object.Equals (object) и object.GetHashCode (), определяя операторы == и! = Для типа ConstrainedNumber<T> и реализуя IEquatable<T> и IEquatable<ConstrainedNumber<T>>. Я также рекомендую определить конструктор копирования для базового класса и всех производных типов. Затем клонирование может быть чисто реализовано в базовом классе путем извлечения конструктора копирования с помощью отражения, но это совершенно необязательно. Вы можете сами разобраться в реализации ConstrainedNumber<T>, если только я не разместил ее где-нибудь в stackoverflow.

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

public sealed class ReturnValue: ConstrainedNumber<int>
{
    public static readonly NumberConstraint<int> constraint = (int x) => (x >= 0 && x < 3);

    public static readonly ReturnValue Success = new ReturnValue(0);
    public static readonly ReturnValue FailReason1 = new ReturnValue(1);
    public static readonly ReturnValue FailReason2 = new ReturnValue(2);

    private ReturnValue( int value ): base( value, constraint ) {}
    private ReturnValue( ReturnValue original ): base (original) {} //may be used to support IClonable implementation in base class
    public static explicit operator ReturnValue( int value )
    {
        switch(value) //switching to return an existing instance is more efficient than creating a new one and re-checking the constraint when there is a limited number of allowed values; if the constraint was more generic, such as an even number, then you would instead return a new instance here, and make your constructors public.
        {
            case 0: return Success;
            case 1: return FailReason1;
            case 2: return FailReason2;
        }
        throw new ArgumentException( "Value fails to meet the constraint defined for " + typeof(ReturnValue).FullName + ".", "value" );
    }

}

Вы можете использовать эту технику для любых ограничений. Например, класс EvenNumber может иметь ограничение, которое возвращает true, если заданное число четное. В этом случае вы просто сделаете ваши конструкторы общедоступными и упростите свой оператор статического преобразования, чтобы просто вернуть новый EvenNumber, вместо переключения для возврата одного из ограниченных существующих экземпляров.

Можно использовать так:

EvenNumber x = (EvenNumber)2;
EvenNumber y = (EvenNumber)3; //throws exception "Value fails to meet the constraint defined for {namespace}.EvenNumber."  A c# enum would stupidly allow such a cast, creating an invalid EvenNumber, breaking the object-oriented model
int z = x; //implicit conversion, no cast necessary;
3 голосов
/ 23 февраля 2009

Неявное приведение не существует, поскольку перечисление не должно использовать int в качестве базового типа. Если ваше перечисление использовало, например, uint в качестве базового типа, то неявное приведение из uint к int отсутствует.

2 голосов
/ 23 февраля 2009

Перечисления и целые числа просто неявно преобразуются в соответствии со спецификацией (за исключением литерала 0, который разрешен для сравнительных тестов / заданий / и т. Д.). Однако явное приведение - это все, что нужно.

0 голосов
/ 01 октября 2015

Как насчет использования статических членов класса?

//enum DocInfos { DocName, DocNumber, DocVersion};
public class DocInfos
{
    public static int DocName = 0;
    public static int DocNumer = 1;
    public static int DocVersion = 2;
}

...

            Doc = new string[DocInfos.DocVersion];
            // Treffer
            Doc[DocInfos.DocName] = TrimB(HTMLLines[lineCounter + 2])

...

0 голосов
/ 23 февраля 2009

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

В связи с вашим вопросом необходимо рассмотреть два отдельных вопроса:

  1. Значение Enum не может рассматриваться как int по умолчанию, потому что тогда вы сможете предоставить любое целое число, и не будет никакой проверки времени компиляции, чтобы проверить, что предоставленное целое число делает фактически существует как значение в Перечислении.

  2. Приведение становится необходимым, поскольку вы пытаетесь преобразовать управляющий тип (типа YourCustomEnum, производный от класса System.Enum) в базовый тип, т. Е. int или byte и т. Д. .

0 голосов
/ 23 февраля 2009

Как ни странно, это относится не только к .NET Framework, но и к C #. Как уже отмечали другие комментаторы, в C # это в основном спецификация языка. То же самое не верно в VB.NET.

Проверьте справочную страницу MSDN для Перечисления в VB.NET . Обратите внимание, что вы можете указать тип данных перечисления во время объявления Enum.

Это означает, что если вы действительно не хотите засорять свой код приведениями к (int), вы можете написать свое перечисление в VB.NET, объявить его как целое число, а затем использовать этот Enum из C #.

Помните, как нам сказали, что компьютеры сделают нашу жизнь намного проще? :)

0 голосов
/ 23 февраля 2009

Нет, вы не можете избежать кастования; почему нет неявного преобразования, я не знаю, но нет.

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