字符串常量被高估了吗?

时间:2009-06-04 00:11:33

标签: string constants

很容易忘记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...
}

9 个答案:

答案 0 :(得分:11)

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

答案 1 :(得分:5)

对于类似的东西,它取决于常量的使用频率。如果它只是在你的例子中的一个地方,那么硬编码是好的。如果它在许多不同的地方使用,肯定使用常量。如果你不小心,一个错字可能会导致数小时的调试,因为你的编译器不会注意到你输入“Tsate”而不是“Taste”,而它会注意到你输入了fruit_node.Tsate而不是fruit_node。味道。

编辑: 我现在看到你提到了复制和粘贴,但如果你这样做,你可能也会因为不首先创建一个常量而浪费你节省的时间。通过智能感知和自动完成功能,您可以通过几次击键获得常量,而不是经历复制/粘贴的麻烦。

答案 2 :(得分:0)

你可能已经猜到了。答案是:它取决于具体情况。

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

如果它是一个庞大而复杂的系统的一部分,并且常量将用于多个文件,我会更多地选择第二个选项。

答案 3 :(得分:0)

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

但两者都是允许的,因为有时他们不是邪恶。你需要自己做出决定 - 这会带来更清晰的代码吗?这对维护者来说会更好吗?原规则背后的推理是否适用于我的情况?如果我以后必须阅读或维护这段代码,我怎么能说它是写的呢?

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

答案 4 :(得分:0)

就个人而言,我会事先从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 
    }
}

这样,“水果”与存储无关。

答案 5 :(得分:0)

我会选择常数。这是 little 更多的工作,但没有性能影响。即使你经常复制/粘贴这些值,我肯定有一些实例,当我输入时我改变了代码并且没有意识到Visual Studio有焦点。我很多更喜欢这些导致编译错误。

答案 6 :(得分:0)

对于给出的示例,将字符串用作地图或字典的键,我倾向于使用enum(或其他对象)。你可以使用枚举而不是常量字符串来做更多事情。此外,如果某些代码被注释掉,IDE在执行重构时通常会错过。此外,对注释中的String常量的引用可能包含也可能不包含在重构中。

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

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

答案 7 :(得分:0)

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

答案 8 :(得分:0)

是的,差不多。

我认为静态类型语言的开发人员对任何动态的东西都有不健康的恐惧。动态类型语言中的每一行代码实际上都是一个字符串文字,并且它们已经好多年了。例如,在技术上用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的外部用户希望使用这些字符串值来处理它(即它们仅用于内部数据操作),可读代码比可重构代码更有价值,所以我只是将文字直接内联。同样,这是假设我明确了解您的确切用例。

有人似乎没有注意到的一点是,一旦你定义了一个常数,它的范围就变成了维持和思考的东西。这实际上确实有成本,它并不像所有人想象的那样免费。考虑一下:

我班上应该是私人还是公共?如果某个其他命名空间/包需要相同的值,那么我现在应该将常量提取到一些全局静态常量类吗?如果我现在需要在其他组件/模块中,我可以进一步提取它吗?所有这些都使代码变得越来越不易读,难以维护,使用起来不那么愉快,而且更复杂。全部都是以可重构性的名义?

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