Могу ли я избежать использования значения перечисления при попытке использовать или вернуть его?
Если у меня есть следующее перечисление:
public enum ReturnValue{
Success = 0,
FailReason1 = 1,
FailReason2 = 2
//Etc...
}
Могу ли я избежать кастинга, когда вернусь, например:
public static int main(string[] args){
return (int)ReturnValue.Success;
}
Если нет, то почему по умолчанию значение enum не рассматривается как int?
Ответы
Ответ 1
перечисления должны быть безопасными по типу. Я думаю, что они не делали их неявно литыми, чтобы препятствовать другим видам использования. Хотя структура позволяет вам присваивать им постоянное значение, вы должны пересмотреть свои намерения. Если вы в основном используете перечисление для хранения постоянных значений, рассмотрите использование статического класса:
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;
}
ИЗМЕНИТЬ
Если вы хотите предоставить значения для перечисления, это когда вы хотите их объединить. См. Пример ниже:
[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;
}
}
Ответ 2
Перечень С# бесполезен.
Вы можете избежать кастинга из своего типа И ограничить значения, которые могут быть явно применены к вашему типу, путем создания закрытого класса и предоставления неявных/явных операторов преобразования.
- Предоставить неявный оператор для преобразования из вашего типа в общий int, поэтому вам не нужно делать бросок.
- Предоставляет явный оператор для преобразования из int в ваш тип, который выдает ошибку, если целое число не отвечает ограничению, например (int x) = > (x >= 0 && x <= = 2).
При использовании этого метода создайте универсальный неизменный базовый класс, такой как 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.
Вы можете указать именованные статические значения readonly в своем производном ConstrainedNumber, чтобы вы могли обращаться к ним так же, как перечисление.
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
Нет никакого неявного приведения, потому что перечисление не должно использовать int в качестве базового типа. Если в вашем перечислении используется uint в качестве базового типа, например, нет никакого неявного перевода из uint в int.
Ответ 4
Перечисления и ints просто неявно не могут быть закрыты в соответствии с спецификацией (кроме литерала 0, который разрешен для сравнительных тестов/присвоений/etc). Явное приведение - это все, что необходимо.
Ответ 5
Нет, вы не можете избежать кастинга; о том, почему нет явного преобразования, я не знаю, но там нет.
Ответ 6
Как ни странно, это не относится к .NET Framework, а только к С#. Как уже отмечали другие комментаторы, в С# это в основном спецификация языка. То же самое не относится к VB.NET.
Зайдите на страницу справки MSDN для Перечисления в VB.NET. Обратите внимание, что вы можете указать тип данных перечисления на время объявления Enum.
Это означает, что если вы действительно не хотите, чтобы ваш код был вытеснен с помощью меток (int), вы могли бы написать свое перечисление в VB.NET, объявить его как целое число, а затем использовать этот Enum из С#.
Помните, как они сказали, что компьютеры сделают нашу жизнь намного проще?:)
Ответ 7
Вы можете приписать это поведение основной идее создания Enumerations... для создания набора именованных констант, которые могут иметь только указанные (или по умолчанию) значения в зависимости от базового типа.
Есть два отдельных вопроса для рассмотрения, связанные с вашим вопросом:
-
Значение Enum
не может рассматриваться как int по умолчанию, потому что тогда вы сможете предоставить любое целое число, и не было бы проверки времени компиляции, чтобы проверить, что предоставленное целое число фактически существует как значение в Перечислении.
-
Кастинг становится необходимым, поскольку вы пытаетесь преобразовать из управляющего типа (типа YourCustomEnum
, который происходит из класса System.Enum
) в базовый тип, т.е. int
или byte
и т.д..
Ответ 8
Как насчет использования статических членов класса?
//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])
...