Является ли использование неявных полей enum для представления числовых значений плохими практиками?
Является ли использование неявных полей enum для представления числовых значений обязательно плохой практикой?
Вот пример использования: я хочу простой способ представлять шестнадцатеричные цифры, а поскольку перечисления на С# основаны на целых числах, они кажутся естественным совпадением. Мне здесь не нравится char
или string
, потому что я должен явно проверить их значения. Проблема с перечислениями состоит в том, что цифры [0-9]
не являются допустимыми идентификаторами полей (без уважительной причины). Мне пришло в голову, что мне не нужно объявлять цифры 0-9
, потому что они неявно присутствуют.
Итак, мое шестнадцатеричное перечисление будет выглядеть так:
public enum Hex : int {
A = 10,
B = 11,
C = 12,
D = 13,
E = 14,
F = 15
}
Итак, я мог написать Tuple<Hex,Hex> r = Tuple.Create(Hex.F,(Hex)1);
, а r.Item1.ToString() + r.Item2.ToString()
дал бы мне "F1". В основном, мой вопрос заключается в том, что если значение ToString()
числовой константы - это то, что я хочу назвать поле перечисления, почему проблематично полностью опустить объявление?
Альтернативное представление в качестве перечисления может иметь объявленные поля с некоторым префиксом, например:
public enum Hex : int {
_0 = 0,
_1 = 1,
_2 = 2,
_3 = 3,
_4 = 4,
_5 = 5,
_6 = 6,
_7 = 7,
_8 = 8,
_9 = 9,
A = 10,
B = 11,
C = 12,
D = 13,
E = 14,
F = 15
}
Проблема в том, что приведенный выше пример дал бы мне "F_1" вместо "F1". Очевидно, это легко исправить. Мне интересно, есть ли дополнительные проблемы с неявным подходом, который я не рассматриваю.
Ответы
Ответ 1
Это фундаментально сломанный способ обработки hex. Hex - это детализация интерфейса человека. Это всегда строка, представляющая число. Подобно "1234" - это представление значения 1234. Оно представляется "4D2", если оно представлено в шестнадцатеричном виде, но число в вашей программе все равно 1234. Программа должна только когда-либо относиться к числу, а не к представлению.
Преобразование числа в hex должно происходить только при отображении номера в глаза человека. Простой подход к ToString ( "X" ). И для того, чтобы выполнить синтаксический анализ с пользовательского ввода с помощью TryParse() с помощью NumberStyles.HexNumber. Ввод и вывод, ни в какой другой точке, вы никогда не будете иметь дело с шестнадцатеричным.
Ответ 2
Это плохая практика, потому что это умный трюк, который удивляет людей, которые читают ваш код. Это меня удивило, что это действительно сработало, мне было сказано wtf. Помните единственное допустимое измерение качества кода:
![enter image description here]()
Умные трюки не принадлежат коду, который должен читаться и поддерживаться другими. Если вы хотите вывести число как hex, преобразуйте его в шестнадцатеричную строку, используя обычный String.Format("{0:X}", value)
Ответ 3
Я бы определил a struct
для HexDigit
. Вы можете добавить HexDigit
'A' в 'F' в качестве статических констант (или статических полей readonly).
Вы можете определить неявные преобразователи, чтобы разрешить преобразование целых чисел 0-9, преобразование в целые числа, и вы можете переопределить ToString(), чтобы заставить вас Tuples выглядеть хорошо.
Это будет гораздо более гибким, чем перечисление.
Ответ 4
По-моему, это плохая практика. Если вам нужно представление Hex, просто создайте вспомогательный класс, который обрабатывает все необходимые операции.
Как в этой статье, эти фрагменты кода помогут в создании помощников:
// Store integer 182
int decValue = 182;
// Convert integer 182 as a hex in a string variable
string hexValue = decValue.ToString("X");
// Convert the hex string back to the number
int decAgain = int.Parse(hexValue, System.Globalization.NumberStyles.HexNumber);
Причина, по которой я считаю, что это плохая практика, заключается в том, что она не ориентирована на объекты, и она сталкивается с проблемой полагаться на перечисление, чтобы перевести все значения жестко закодированные - что плохо. Если вы можете избежать жесткого кодирования, это всегда шаг в правильном направлении. Кроме того, класс-помощник является расширяемым и со временем может быть улучшен для дополнительной функциональности.
Говоря, я имею в виду простоту перечислений, но, опять же, это не отменяет необходимости держать вещи OO по-моему.
Ответ 5
Я не уверен, что вы на самом деле пытаетесь выполнить здесь, но если вы хотите ограничить что-то двумя шестнадцатеричными цифрами, почему бы вам просто не объявить его байтом? В то время как ваш enum hack умный, я на самом деле не вижу необходимости в нем. Это также может быть неправильно истолковано, если передать другому программисту без объяснения причин, так как ваше использование необъявленных значений против вашего перечисления является противоречивым.
Что касается оснований чисел и буквенных представлений, целое число в вычислении не является основанием-10 или базовым-16 изначально, оно фактически основано-2 (двоичное) под обложками, а любые другие представления - удобство для человека. Язык уже содержит способы представления буквенных чисел как в десятичном, так и в шестнадцатеричном формате. Ограничение числа - это функция правильного выбора типа.
Если вы вместо этого пытаетесь каким-то образом ограничить любое произвольное количество шестнадцатеричных цифр, возможно, просто инициализировать такой массив байтов, как это было бы более целесообразно:
byte[] hexBytes = new byte[3] { 0xA1, 0xB2, 0xC3 };
Кроме того, сохраняя ваше значение как обычный числовой тип или используя байтовый массив, а не помещая его в Tuples с перечислениями, вы сохраняете простой доступ ко всему диапазону операций, которые в противном случае становятся более трудными.
Что касается ограничения ваших чисел на произвольные нечетные количества шестнадцатеричных цифр, вы можете выбрать тип, который содержит хотя бы ваше желаемое значение + 1 цифру и ограничить значение во время выполнения. Возможна следующая реализация:
public class ThreeNibbleNumber
{
private _value;
public ushort Value
{
get
{
return _value;
}
set
{
if (value > 4095)
{
throw new ArgumentException("The number is too large.");
}
else
{
_value = value;
}
}
}
public override string ToString()
{
return Value.ToString("x");
}
}
В одном из ваших комментариев к другому ответу вы ссылаетесь на идею создания CSS-цветов. Если это то, что вы хотите, такое решение кажется подходящим:
public struct CssColor
{
public CssColor(uint colorValue)
{
byte[] colorBytes = BitConverter.GetBytes(colorValue);
if (BitConverter.IsLittleEndian)
{
if (colorBytes[3] > 0)
{
throw new ArgumentException("The value is outside the range for a CSS Color.", "s");
}
R = colorBytes[2];
G = colorBytes[1];
B = colorBytes[0];
}
else
{
if (colorBytes[0] > 0)
{
throw new ArgumentException("The value is outside the range for a CSS Color.", "s");
}
R = colorBytes[1];
G = colorBytes[2];
B = colorBytes[3];
}
}
public byte R;
public byte G;
public byte B;
public override string ToString()
{
return string.Format("#{0:x}{1:x}{2:x}", R, G, B).ToUpperInvariant();
}
public static CssColor Parse(string s)
{
if (s == null)
{
throw new ArgumentNullException("s");
}
s = s.Trim();
if (!s.StartsWith("#") || s.Length > 7)
{
throw new FormatException("The input is not a valid CSS color string.");
}
s = s.Substring(1, s.Length - 1);
uint color = uint.Parse(s, System.Globalization.NumberStyles.HexNumber);
return new CssColor(color);
}
}
Ответ 6
Я не особо понимаю, почему вы хотите это сделать, но вы можете использовать атрибут Description для каждого из ваших значений enum, чтобы избавиться от _ и создать какую-то статическую функцию, которая позволяет вам получить один из ваши значения перечислены легко, как Hex (15) → 'F'.
public enum Hex {
[Description("0")] _0 = 0,
...
}