15

使用隐式枚举字段来表示数值一定是不好的做法吗?

这是一个用例:我想要一种简单的方法来表示十六进制数字,并且由于 C# 枚举基于整数,因此它们看起来很自然。我不喜欢 achar或 astring这里,因为我必须明确地验证它们的值。枚举的问题是数字[0-9]不是有效的字段标识符(有充分的理由)。我突然想到我不需要声明 numbers 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”。显然,这很容易解决。我想知道我没有考虑的隐式方法是否还有其他问题。

4

6 回答 6

13

这是不好的做法,因为它是一个聪明的技巧,让阅读您的代码的人感到惊讶。令我惊讶的是它确实有效,它让我说 wtf. 记住唯一有效的代码质量衡量标准:

在此处输入图像描述

聪明的技巧不属于应该由他人阅读和维护的代码。如果要将数字输出为十六进制,请使用普通将其转换为十六进制字符串String.Format("{0:X}", value)

于 2012-10-09T02:37:22.457 回答
7

这是处理十六进制的根本方法。Hex 是一个人机界面细节。它始终是一个字符串,一个数字的表示。就像“1234”是值 1234 的表示。当它以十六进制表示时恰好是“4D2”,但程序中的数字仍然是 1234。程序应该只关注数字,而不是表示。

仅当您将数字显示给人眼时才应将数字转换为十六进制。使用 ToString("X") 很简单。并使用 NumberStyles.HexNumber 使用 TryParse() 从人工输入中解析回来。输入和输出,在其他任何时候你都不应该处理十六进制。

于 2012-10-05T00:15:08.680 回答
3

我会定义一个structfor HexDigit。您可以将HexDigit“A”添加到“F”作为静态常量(或静态只读字段)。

您可以定义隐式转换器以允许转换整数 0-9、转换为整数,并且您可以覆盖 ToString() 以使元组看起来不错。

这将比枚举灵活得多。

于 2012-10-04T21:48:55.710 回答
1

在我看来,这是一种不好的做法。如果您需要十六进制表示,只需创建一个帮助类来处理您需要的所有操作。

正如本文所建议的,这些代码片段将有助于创建助手:

// 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 的需要。

于 2012-10-12T13:44:44.660 回答
0

我不确定您在这里实际上要完成什么,但是如果您希望将某些内容限制为两个十六进制数字,为什么不将其声明为一个字节?虽然您的 enum hack 很聪明,但我实际上并不认为需要它。如果在没有解释的情况下将其传递给另一个程序员,也可能会被误解,因为您对枚举使用未声明的值是违反直觉的。

关于数字基数和文字表示,计算中的整数本身不是base-10或base-16,它实际上是base-2(二进制),并且任何其他表示都是人类方便。该语言已经包含以十进制和十六进制格式表示文字数字的方法。Limiting the number is a function of appropriately choosing the type.

如果您试图将某些内容限制为任意偶数的十六进制数字,则可能像这样简单地初始化一个字节数组会更合适:

    byte[] hexBytes = new byte[3] { 0xA1, 0xB2, 0xC3 };

此外,通过将您的值保持为常规数字类型或使用字节数组而不是将其放入带有枚举的元组中,您可以轻松访问整个范围的操作,否则会变得更加困难。

关于将数字限制为任意奇数的十六进制数字,您可以选择至少包含所需值 + 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);
        }
    }
于 2012-10-10T15:56:02.397 回答
0

我并不特别明白为什么要这样做,但是您可以在每个枚举值上使用 Description 属性来摆脱 _ 并创建某种静态函数,该函数允许您获取其中一个枚举值很容易像 Hex(15) -> 'F'。

public enum Hex {
    [Description("0")] _0 = 0,
    ...
}
于 2012-10-12T04:22:11.800 回答