【问题标题】:Are string constants overrated?字符串常量是否被高估了?
【发布时间】:2009-06-04 00:11:33
【问题描述】:

很容易忘记015 等奇数。当我编写低级 C 代码时,我曾经对此非常严格。随着我更多地使用涉及 XML 和 SQL 的所有字符串文字,我发现自己经常打破在代码中嵌入常量的规则,至少在字符串文字方面是这样。 (我仍然擅长数字常量。)

字符串与数字不同。创建一个与其值同名的编译时常量(例如const string NameField = "Name";)感觉乏味且有点愚蠢,尽管在许多位置重复相同的字符串文字似乎有风险,但打字错误的可能性很小多亏了复制和粘贴,当我重构时,我通常会进行全局搜索,这不仅涉及更改事物的名称,还包括更改与周围事物相关的功能处理方式。

所以,假设您没有一个好的 XML 序列化程序(或者没有心情设置一个)。您个人会使用以下哪一项(如果您不想在某些代码审查中屈服于同行压力):

static void Main(string[] args)
{
    // ...other code...

    XmlNode node = ...;

    Console.WriteLine(node["Name"].InnerText);
    Console.WriteLine(node["Color"].InnerText);
    Console.WriteLine(node["Taste"].InnerText);

    // ...other code...
}

或:

class Fruit
{
    private readonly XmlNode xml_node;

    public Fruit(XmlNode xml_node)
    {
        this.xml_node = xml_node;
    }

    public string Name
    { get { return xml_node["Name"].InnerText; } }

    public string Color
    { get { return xml_node["Color"].InnerText; } }

    public string Taste
    { get { return xml_node["Taste"].InnerText; } }
}

static void Main(string[] args)
{
    // ...other code...

    XmlNode node = ...;
    Fruit fruit_node = new Fruit(node);

    Console.WriteLine(fruit_node.Name);
    Console.WriteLine(fruit_node.Color);
    Console.WriteLine(fruit_node.Taste);

    // ...other code...
}

【问题讨论】:

  • 0 不是奇数,是偶数 :-)

标签: string constants


【解决方案1】:

定义的常量更容易重构。如果“Name”最终被使用了 3 次,而您将其更改为“FullName”,则更改常量是一次更改而不是三次。

【讨论】:

  • OP 的观点是全局搜索和替换(在 IDE 或文本编辑器中)将在一次操作中捕获所有三个。
  • @Eddie,它还会捕获“Name”的所有其他使用 const string XmlNodeName = "Name";常量字符串地址名称=“名称”;常量字符串 XmlFruitName = "名称";如果您在各处硬编码“名称”并且只有 XmlNodeName 发生了变化怎么办?
  • 是的,全局搜索和替换比使用硬编码字符串更危险。当它们被用于替换硬编码字符串时更是如此。
  • @Dour High Arch:是的,我同意你的看法。我只是指出了问题的 OP 上下文。
【解决方案2】:

对于类似的事情,这取决于常量的使用频率。如果按照您的示例仅在一个地方,那么硬编码就可以了。如果它在许多不同的地方使用,一定要使用常量。如果您不小心,一个错字可能会导致数小时的调试,因为您的编译器不会注意到您输入的是“Tsate”而不是“Taste”,而它会注意到您输入的是fruit_node.Tsate 而不是fruit_node。尝尝。

编辑: 我现在看到您提到了复制和粘贴,但是如果您这样做,您可能还会因为一开始就没有创建常量而浪费了您节省的时间。借助智能感知和自动完成功能,您只需敲几下键就可以得到常量,而不必费力地复制/粘贴。

【讨论】:

    【解决方案3】:

    正如您可能猜到的那样。答案是:这取决于上下文。

    这取决于示例代码的一部分。如果它只是一个小型一次性系统的一部分,那么对常量进行硬编码可能是可以接受的。

    如果它是大型复杂系统的一部分,并且常量将在多个文件中使用,我会更倾向于第二种选择。

    【讨论】:

      【解决方案4】:

      与许多编程问题一样,这是一个品味问题。正确编程的“法则”是从经验中创造出来的——许多人被全局变量所困扰,导致命名空间或清晰度问题,所以全局变量是邪恶的。许多人使用了幻数,但后来发现这个数字是错误的或需要更改。文本搜索不适合更改这些值,因此代码中的常量是邪恶的。

      但两者都是允许的,因为有时它们并不是邪恶的。您需要自己做出决定——这会导致更清晰的代码?哪个对维护者更好?原始规则背后的推理是否适用于我的情况?如果我以后必须阅读或维护这段代码,我宁愿如何编写它?

      良好的编码风格没有绝对的规律,因为没有两个程序员的思维方式完全相同。规则是尽可能编写最清晰、最干净的代码。

      【讨论】:

        【解决方案5】:

        就我个人而言,我会提前从 XML 文件中加载水果 - 类似于:

        public class Fruit
        {
            public Fruit(string name, Color color, string taste)
            {
                this.Name = name;  this.Color = color; this.Taste = taste;
            }
        
            public string Name { get; private set; }
            public Color Color { get; private set; }
            public string Taste { get; private set; }
        }
        
        // ... In your data access handling class...
            public static FruitFromXml(XmlNode node) 
            { 
                    // create fruit from xml node with validation here 
            }
        }
        

        这样,“水果”就不会真正与存储绑定。

        【讨论】:

        • 好建议,但不是问题的答案。
        • @jmucchiello:是和否 - 这是答案,因为我也不会这样做。它不是一个真正的常数,它是对一个值的 Xml 节点“查询”的屏蔽行为。这两个选项都不是我喜欢的代码,所以我输入了我喜欢的选项。
        【解决方案6】:

        我会选择常量。这是一个一点的工作,但对性能没有影响。即使您通常复制/粘贴这些值,我也确实遇到过我在键入时更改代码并且没有意识到 Visual Studio 具有焦点的情况。我非常更喜欢这些导致编译错误。

        【讨论】:

          【解决方案7】:

          对于给出的示例,其中字符串用作映射或字典的键,我倾向于使用 enum(或其他对象)来代替。与使用常量字符串相比,使用枚举通常可以做更多的事情。此外,如果某些代码被注释掉了,IDE 在进行重构时经常会错过它。此外,对 cme​​ts 中的 String 常量的引用可能包含在重构中,也可能不包含在重构中。

          当字符串将在多个位置使用,字符串很长或复杂(例如正则表达式)时,或者当正确命名的常量会使代码更明显时,我将为字符串创建一个常量。

          我更喜欢我的拼写错误、不完整的重构和其他此类错误无法编译,而不是无法正常运行。

          【讨论】:

            【解决方案8】:

            与许多其他重构一样,这是一个可以说是可选的附加步骤,它使您的代码维护风险更小,并且更容易被“下一个人”理解。如果您处于奖励这种事情的情况(我所做的大部分事情),那就去做吧。

            【讨论】:

              【解决方案9】:

              是的,差不多。

              我认为静态类型语言的开发人员对任何动态的东西都有一种不健康的恐惧。几乎动态类型语言中的每一行代码实际上都是字符串文字,而且它们多年来一直很好。例如,在 JavaScript 技术上是这样的:

              var x = myObject.prop1.prop2;
              

              相当于这个:

              var x = window["myObject"]["prop1"]["prop2"]; // assuming global scope
              

              但绝对不是在 JavaScript 中这样做的标准做法:

              var OBJ_NAME = "myObject";
              var PROP1_NAME = "prop1";
              var PROP2_NAME = "prop2";
              
              var x = window[OBJ_NAME][PROP1_NAME][PROP2_NAME];
              

              那太荒谬了。

              这仍然取决于,比如如果一个字符串在很多地方使用并且输入起来相当麻烦/丑陋(“name”与“my-custom-property-name-x”),那么它可能值得做一个常量,即使在单个类中(此时最好在类中保持内部一致并使所有其他字符串也成为常量)。

              此外,如果您确实打算让其他外部用户使用这些常量与您的库进行交互,那么定义可公开访问的常量并记录用户应该使用这些常量与您的库进行交互也是一个好主意。但是,通过魔术字符串常量进行交互的库通常是一种不好的做法,您应该考虑以一种不需要使用魔术常量与之交互的方式来设计您的库。

              我认为在您给出的具体示例中,字符串的键入相对简单,并且可能没有您的 API 的外部用户希望使用这些字符串值来使用它(即它们仅用于内部数据操作),可读代码比可重构代码更有价值,所以我只会将文字直接内联。同样,这是假设我具体了解您的确切用例。

              似乎没有人注意到的一件事是,一旦您定义了一个常量,它的范围就变成了需要维护和思考的东西。这实际上确实是有代价的,它不像每个人都认为的那样免费。考虑一下:

              在我的课堂上应该是私有的还是公开的?如果其他命名空间/包需要相同的值怎么办,我现在应该将常量提取到某个全局静态常量类吗?如果我现在在其他程序集/模块中需要它,我应该进一步提取它吗?所有这些都使代码的可读性越来越差,更难维护,使用起来更不愉快,而且更复杂。一切都是为了可重构性?

              通常,这些“伟大的重构”永远不会发生,当它们发生时,无论如何都需要使用所有新字符串进行完全重写。如果你在这个伟大的重构之前使用了一些共享模块(如上一段),它没有你现在需要的这些新字符串,那么怎么办?您是否将它们添加到相同的常量共享模块中(如果您无权访问此共享模块的代码怎么办)?或者您是否将它们保留在您的本地,在这种情况下,现在有多个分散的字符串常量存储库,都在不同的级别,冒着代码中重复常量的风险?一旦你达到了这一点(相信我,我已经看到了),重构就变得没有意义了,因为虽然你会得到 your 常量的所有 your 用法,但你'会错过其他人对他们的常量的使用,即使这些常量与您的常量具有相同的逻辑值并且您实际上正试图更改所有这些常量。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2016-02-18
                • 1970-01-01
                • 1970-01-01
                • 2011-07-02
                • 2015-09-17
                • 2011-05-28
                相关资源
                最近更新 更多