将某些值存储为字符串是不好的做法吗?


19

那是一个非常模糊的标题,但我想不出一种更好的措词。但是,作为一个示例,请考虑游戏中角色的前进方向。先使用字符串然后再执行类似操作,这是一种错误if(character.direction == "left")。在我看来,它为愚蠢的错误留了太多的空间,例如不小心使用Leftl或而不是left。我的怀疑正确吗?如果是这样,实现这种目标的首选方式是什么?


10
您的意思是,除了枚举之外,您的语言是否支持这些枚举?
hoffmale

12
您是说Stringed Typed吗?
RubberDuck


某些语言不能将其他值存储为字符串,例如bash
mouviciel 2016年

不知道为什么,但我记得C中的“定义”。您可以执行以下操作:#define false true
linuxunil 2016年

Answers:


28

如果您使用的语言支持枚举的使用,我会使用它们。它允许您限制给定类型可用的选项数量。例如在Java中:

public enum Direction {
    LEFT,
    RIGHT,
    UP,
    DOWN
}

2
该答案无助于理解OP问题。我在这里看不到任何推理,仅是一种意见,范围仅限于enum像Java这样的class。许多语言(例如VBA)都具有 enum,但是由于缺少相同的面向未来的证明,因此它并不是最佳选择。请参阅我的答案,以获取有关enum仅为何为何是一个不完整,通常是脆弱且针对特定语言的解决方案的讨论。
sqykly

12

在代码中使用文字字符串(或幻数)是一种可怕的做法。

枚举很好,或者至少使用常量(枚举当然只是一个或多个相关常量的包装类型)。

const string LEFT = "left"
if (someThing == LEFT) { doStuff(); }

这个简单的开关有很多优点,我什至都不知道从哪里开始解释它们(至少没有更多的咖啡)。阅读魔术数字,这是完全相同的概念,只适用于数字值而不是字符串值。

这是一个快速的参考,我敢肯定还有更多:https : //stackoverflow.com/questions/47882/what-is-a-magic-number-and-why-is-it-bad


1
我用这种方法遇到的问题是,您会遇到错字错误,例如--const string LEFT =“ letf”-但是如果您使用的枚举在编译时就被捕获了。
Pieter B

@PieterB-我认为OP的问题足够广泛,“ left”的示例只是一个任意示例-所有情况都与一个枚举不匹配。我同意,对于一组特定的方向,枚举是最佳选择,但是我的回答是更笼统的,因为“如果将其放入文字中,至少使其成为常数”(当然,枚举是常量的类型)
jleach,2013年

3
我很高兴使用将周末的日期指定为以“ s”开头的那几天的应用程序。他们对“事实”感到非常惊讶,他们在国际化申请时说法国只有一个周末。
Pieter B

4
称之为微优化,但是某些语言可能会将其解释为值比较,并浪费大量时间检查字符串中的每个字符。如果您像问问者那样做,并与硬编码的字符串“ left”进行比较,则更有可能。如果不需要将常量值作为字符串(也就是说,您不打印它),则最好使用const int。
Devsman '16

4

简短的答案:是的,除了存储和访问文本字符序列以外,字符串不是任何任务的理想选择,即使抽象的基础位是字符串,将它们引用为变量或常量也有好处

答案很长:大多数语言提供的类型都更接近您的问题所在,即使不是,您也可以使用某些方法将其定义left为不同于right(等)的实体。通过使用将其转换为字符串"left"仅会丧失该语言和IDE的有用功能(例如错误检查)。即使你必须使用

left = "left";

或类似的东西,当您在代码中引用字符串时,它具有使用字符串的优势。例如,

if (player.direction == Left)

会被检测为编译时错误(最好的情况,java等),或者由值得将其位视为错误的所有IDE标记为错误(最坏的情况,基本等)。

此外,使用“ left作为可引用实体” 的概念将帮助您适应您决定要使用方向类型的任何方向。通过使用"left",方向致力于成为一个String。如果您为该类型找到或创建了更好的抽象,则需要遍历整个代码体,以更改每个的实例"left"。如果详细说明了类型,则常量或变量将不需要任何更改。

您真正想要的类型特定于您的域。您打算如何处理您的代码left?也许您需要镜像朝左或向前移动的纹理或精灵的x轴;如果是这样,您可能希望left使用具有反映该行为的属性或方法的right对象,而该对象具有相反的非镜像结果。你可以这样做的逻辑在代表精灵或纹理,在这种情况下,你又将会使用遭受对象"left",而不是left出于上述原因。

在Java(以及我可能还不知道的其他语言)中,enum是一个很好的选择,因为在Java中,enumfinal class与有限的实例集一样有用。您可以根据需要定义行为,也可以切换到一个开放类,而在声明的文件之外没有任何麻烦enum,但是许多语言将an enum作为基本类型或要求使用特殊语法。这会使enum与之无关的代码暴露在外,以后可能需要更正。enum在考虑使用该选项之前,请确保您了解语言的概念。


2

您没有指定语言,但是要给出一个通用的答案,您应该在实际需要文本时使用字符串,而不是代表某些东西。例如,您给出的示例在生产软件中根本不可接受。

每种语言都有各种基本数据类型,您应该使用最适合该任务的一种。要使用您的示例,您可以使用数字常量来表示不同的状态。因此left的值为零,right的值为1。除了您提到的原因之外,数字值所占用的空间(内存)更少,并且比较数字总是比比较多个字符串快。

根据建议,如果您的语言允许,请使用枚举。在您的特定示例中,这显然是更好的选择。


速度和内存不太重要。也就是说,更喜欢枚举或常量。如果数据来自文本格式(例如XML),则字符串更有意义,但即使如此,也应与所有大写字母或所有大写字母保持一致。或者,始终进行转换,例如。if (uppercase(foo) == "LEFT")
user949300 '16

2
@ user949300将字符串转换为全部大写或全部小写也不可靠。有可能某人可能决定访问另一个区域(或在土耳其扩展业务),例如土耳其,并中断幼稚的上套管(或下套管)字符串比较。
8bittree '16

关心速度和内存始终是一种好习惯,它们不是无限的资源。尽管出于可读性或其他折衷目的,有意识地牺牲它是可以的,但在这里情况并非如此。但是不关心它们很容易导致应用程序呆滞或浪费。
Nagev '16

比较字符串可能比比较枚举慢50倍。大多数时候都没有关系。但是,如果您的代码已经有点慢了,那么这种差异可能会使它无法接受。当然,更重要的是,如果使用字符串,编译器将无法帮助您解决任何拼写错误。
gnasher729

1

只需使用适合您情况的数据类型即可。

使用该string类型作为基础的内部/应用程序内部通信本质上不是问题。实际上,有时最好使用字符串:如果您有一个公共API,则可能需要使该API传入和传出的值是人类可读的。它使使用您的API的人们更容易学习和调试。

您的代码的真正问题在于重复该值。

不要这样做:

if (direction == 'left') {
  goLeft();
}

如果您在其中某些条件条件或“ left”的其他用法中输入了错误,则可能由于键入错误而无法有效地在代码库范围内进行搜索

取而代之的是,针对枚举或常量进行编码-取决于使用您所使用的语言支持哪种数据类型。

这意味着LEFT应在您的应用程序中分配一次分配一次。并且,其余代码应利用这些枚举或常量:

const string LEFT = "left";

if (direction == LEFT) {
  goLeft();
}

如果您愿意的话,这也使您以后可以更轻松地更改用于方向的数据类型。


1

这是不好的做法吗?

也许是的,但是这取决于究竟你在做什么,也对编程语言所使用。

在您的示例中,我们可以推断出方向可能会包含一小部分且永远固定的值。在这种情况下,使用字符串没有优势,但有一定的劣势。对于此示例:

  • enum如果您的编程语言支持,则最好使用类型。
  • 否则,命名整数常量是唯一的定义。

但是,如果一组值是可动态修改的,或者需要随着时间的推移而变化,答案可能会有所不同。在大多数语言中,更改enum类型(例如,添加或删除值)需要完全重新编译。(例如,在Java中删除枚举值将破坏二进制兼容性。)同样,命名整数常量也是如此。

在这种情况下,可能需要其他解决方案,并且使用字符串是一种选择。(并且,如果场景需要具有动态扩展的固定核心,则可以将字符串文字和命名常量结合在一起。)


如果您使用Java实现,character.direction == "left"则出于另一个原因,这是不好的做法。您不应该使用==比较字符串,因为它是在测试对象身份而不是字符串相等性。(如果可以保证"left"字符串的所有实例都已被内插,则可以使用,但是这需要对整个应用程序进行分析。)


1
看起来永远固定不动的东西常常变得根本不那么固定。四个方向可以例如变成八个。
Jimmy T.

@JimmyT。-这取决于上下文。例如,在游戏中,将角色可以从4移动到8的方向更改为数字,将需要彻底修改游戏规则以及实现规则的代码。使用String表示方向将使进行更改所涉及的工作量几乎接近零差异。一个更明智的做法是假设方向的数量不会改变,并承认,你将有很多工作要做,无论哪种方式,如果游戏规则改变,从而从根本上。
斯蒂芬·C

@JimmyT-完全同意,我已经多次看到这种情况,您还可以让开发人员采用重新定义字符串的捷径,例如,product =“ Option”变为product =“ Option_or_Future”完全使代码的含义降低了。
克里斯·米尔本

-3

根据建议,您应该使用枚举。但是,仅使用Enum替代Strigns是不够的恕我直言。在您的特定示例中,玩家速度实际上应该是物理上的矢量:

class Vector {
  final double x;
  final double y;
}

然后,此向量应用于更新每个刻度的玩家位置。

然后,您可以将其与枚举结合使用以提高可读性:

enum Direction {
  UP(new Vector(0, 1)),
  RIGHT(new Vector(1, 0)),
  DOWN(new Vector(0, -1)),
  LEFT(new Vector(-1, 0));

  private Vector direction;

  Direction(Vector v) {
    this.direction = v;
  }

  public Vector getVector() { return this.direction; }
}

4
-1你对他给出的例子太过分了。
Pieter B
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.