为什么代码中的表格布局被认为是错误的原因是什么?

时间:2010-07-22 16:06:18

标签: c# .net coding-style

我的同事告诉我,基于表格的代码格式很糟糕,而且没有可读性,我应遵循惯例。基于表格的格式有什么不好?为什么被禁止?

我问,因为对我来说它更具可读性。

示例(不是真实代码):

if (res == ResultType.Failure)               
  something = ProcessFailure(..);
if (res == ResultType.ScheduledAndMonitored) 
  something = DoSomething(...) && DoSomething3(..);
if (res == ResultType.MoreInfoAvailable)     
  info = GetInfo(..);
if (res == ResultType.OK && someCondition)   
  something = DoSomething2(..);
.... continued

if (res == ResultType.Failure)               something = ProcessFailure(..);
if (res == ResultType.ScheduledAndMonitored) something = DoSomething(...) && DoSomething3(..);
if (res == ResultType.MoreInfoAvailable)     info      = GetInfo(..);
if (res == ResultType.OK && someCondition)   something = DoSomething2(..);
.... continued

为什么我认为第二个更好:

  • 我不需要用眼睛解析文本 - 我一眼就能看到命令的结构。
  • 我立即看到了
    • 有一些ifs和作业
    • 条件中使用的枚举是ResultType,而不再是
    • 只有最后一个条件由两个表达式组成

更新:这不是真正的代码。我只想展示一些例子。考虑它就像是一个旧代码,有人在前一段时间写过,你需要阅读它。为什么第一种格式优先于第二种?

18 个答案:

答案 0 :(得分:13)

当你的同事说你应该遵循惯例时,这是否意味着你的团队有格式约定?如果是这样的话,那就足够了。当每个人以相同的方式格式化他们的代码时,更容易阅读你的队友的代码。

答案 1 :(得分:5)

您描述的“基于表格”的布局在某些情况下非常有用:

public string GetMessageFromErrorCode(int code)
{
    switch (code)
    {
        case 1: return "OK";
        case 2: return "Syntax Error";
        case 3: return "Other error";
    }
}

请注意,此示例是c#规则的有效例外,“所有case语句还必须包含break。”

我不喜欢对代码布局严格要求。遵循编码标准是一件好事。但是一旦你开始做一些事情,比如在函数调用中使用多个带有长名称的参数,编写linq查询,或使用匿名方法或复杂的lambda语句,它可以使你的代码更易于理解违反规则不时。

另见:
Anonymous Methods / Lambda's (Coding Standards)

答案 2 :(得分:5)

原因很简单。

如果你使用空格来缩进所有内容,并使其成为阵容,那么当变量或条件发生变化时保持内联是一件痛苦的事。您必须手动进入并添加/删除空格。

如果您使用制表符缩进所有内容并使其成为阵容,则在每个人的计算机上看起来都不一样,因为不是每个人都将其标签设置为相同的宽度。有些人将它们设置为3个字符,其他人设置为5个字符,其他人设置不同在你的机器上看起来很漂亮,整洁,“像桌子一样”看起来像是乱七八糟。

围绕if执行的语句,并正常格式化。

if (res == ResultType.Failure)               
{
  something = ProcessFailure(..);
}

if (res == ResultType.ScheduledAndMonitored) 
{
  something = DoSomething(...) && DoSomething3(..);
}

if (res == ResultType.MoreInfoAvailable)     
{
  info = GetInfo(..);
}

if (res == ResultType.OK && someCondition)   
{
  something = DoSomething2(..);
}

哦,另一个原因是,并非每个人都使用固定的字体进行编程。信不信由你,一些开发人员使用比例字体。

答案 3 :(得分:3)

我想说有几个原因:要合理地预期维护这样的布局,你需要一个能够为你维护的IDE /插件。如果没有这种支持,开发人员将需要大量的持续努力,成为空间和标签管理员。

第二个也是更具实质性的原因是受此布局影响的代码错误。您的样本可以编写为使用多态,策略模式或其他明显更好的方式,这使得这完全成为一个有争议的问题。

答案 4 :(得分:3)

我在你的例子中看到的问题是眼睛垂直连接信息,例如,当我看到我看到的代码时

something
something
info
something

在心理上将它们分组,在另一个中,我看到了代码的流程和逻辑,然后是细节。这使我更加直接地将我需要的内容塞进我的脑海。

答案 5 :(得分:2)

我认为您的“基于表格”的示例 本身更容易阅读。问题是我们有50年的基于文本的工具来处理源代码作为文本的。您将遇到的两个最明显的问题是:

  1. 我最喜欢的文字处理工具,如diffgrep,都在线上工作。你放在一条线上的次数越多,我的工具就越少。例如,如果我想在if条件下grep某些东西,我必须从ifTrue子句中过滤误报。

    (如果你在可扩展编辑器中使用更常规语法的语言编写,比如Emacs中的Lisp,这不是一个问题,因为遍历实际结构是微不足道的。但是C#非常复杂语法,我不知道用一行或两行代码遍历我的C#代码的简单方法,所以文本工具仍然在这里统治。)

  2. 你最长的行大约是95个字符 - 而..代替实际的args,所以它会更长。真的很长的线条可能是一个痛苦的工作。不同宽度的线条(因为我的大多数线条不会那么长)意味着浪费了屏幕空间,因为我的窗户都是矩形的。或者我必须在滚动时继续调整窗口大小。无论我如何看待它,编辑器和工具都非常适合处理具有多行的文件,但在处理各种不同长度的行时非常糟糕。

  3. 我想写这段代码。我喜欢读这段代码。我真的很讨厌在这段代码上进行三向合并。 : - )

    相比之下,我打算走出去,说我认为到目前为止大部分其他答案都是下铺:

    • 关于char 0x09的各种事情:好吧,使用空格而不是制表符;这些只是常识
    • 等宽字体:如果使用可变宽度字体,我看到的代码标准的所有都会失败,而不仅仅是这个
    • “这是惯例”:好的,但这并没有说明为什么它是约定,或者为什么这种情况不应该是约定的例外(因为我见过的每一组代码约定都允许例外)
    • StyleCop说这很糟糕:你是在为你的工具工作,还是他们为你工作?另见:“这是惯例”
    • 很难维护:不在我的编辑器中,如果你的编辑器很难,你需要学习如何使用它,或者更好地使用它
    • 有些人不会有你的编辑器:你所有的编码标准都取决于用任意懦弱的编辑器编写它是否容易?如果我们雇用一些坚持使用记事本的人,那就是他们的问题,我们不会为他们更改我们的源代码。另见:“很难维护”
    • 它不一致:当然,但是(因为我不是机器人)并不一定会使某些东西难以阅读;我报纸上的每个连环画都有不同的格式(在某些情况下真的不同!),但我发现其中任何一个都难以阅读

    (火焰离开!)

    最后,我认为这两种风格对于这种代码都有点糟糕,原因各不相同。如果您需要一个东西表,那么使用switch,或IDictionary<ResultType,Action>,或列表列表或-tuples(如果订单很重要),或将规则卸载到配置文件中,或者一些东西。你认为桌子看起来像一张桌子是对的。你唯一缺少的是我们有一些结构可以让事情看起来就像在这里工作得很好的表格,if不是其中之一。 : - )

答案 6 :(得分:2)

可读性的关键是一致性。如果相同的构造在不同的时间以不同的方式出现,那么在精神上处理将更加困难。因此,我不想维护以表格样式格式化的C#代码。

另外,标签谎言。

if (!HydrogenTankFull())                          FillHydrogenTank();
if (!OxygenTankFull())                            FillOxygenTank();
if (HydrogenTankFull() && OxygenTankFull())       TestGimbals();
if (GimbalsTested() && LaunchButtonPressed())     IgniteEngine(); BlowRestrainingBolts();
if (LaunchPadCleared())                           TransferControlToHouston();

火箭为什么会摔倒?!

答案 7 :(得分:2)

我认为你有一个很好的例子来制作格式规则的例外,但我的意见对你的事业没有帮助。我的真正建议是,如果你是一个团队,多个人告诉你这样做,但你想做另一个,除了骄傲,你只会失去一点,继续前进,获得一点尊重作为一个团队合作者,这可能会变成让你在未来找到自己的方式。

我通过不遵循它的大部分内容来学习上述内容 - 有时候这不值得头疼。

答案 8 :(得分:2)

我认为这很大程度上取决于个人选择以及使用的格式。在专业的环境中,您希望团队拥有一个明确且商定的编码标准,所有团队成员都遵循该标准。

IDE有很多工具和插件,如Visual Studio,它使您能够通过解析源代码文件来建议对代码结构的更改。

我发现非常有用的一个是[StyleCop] [1]。

我个人觉得Stylecop对于一致的编码指南非常有用。

此致 Nilesh制作

答案 9 :(得分:2)

如果您认为需要对代码进行制表,那么现在是思考的最佳时机。它闻起来像重构时间。代码应该像句子一样可读。

第二个原因是代码维护成本。标准化代码难以维护。

答案 10 :(得分:1)

在自上而下的代码流程中,您的大脑必须切换到此代码块的“表读取模式”。并理解表“列”是什么。并识别表格块的结束并切换到正常读取模式。

IMO太麻烦了。

答案 11 :(得分:1)

如果编程语言,标记语言和文本编辑器都可以相互合作以允许文本格式化以适应代码的方式适应自身,那将是很好的。例如,如果与一行代码关联的注释太长而不适合该行的右侧,并且如果后续的代码行更短,则允许注释包装并保留在那些后续代码行的右侧,用某种箭头或方框表示注释的延续行都属于第一行代码。

不幸的是,我知道没有广泛的编程语言能够很好地嵌入任何嵌入标记的方法。

答案 12 :(得分:1)

即使我发现你的第二个(类似于表格)的例子更具可读性,我也不会使用它,因为

  1. 这种布局不易维护(你可能需要经历整体 列表,如果一行更改)。因此,很可能有人最终会懒得调整它,布局将成为一个难以理解的混乱。
  2. 如果单行有三个或四个条件,所有行将变得不必要很长,因此无法读取。
  3. 当然,如果它违反了团队中的编码标准,请不要这样做:) ...

答案 13 :(得分:1)

着眼于给出的例子,我会像这样格式化。我发现你的例子都难以阅读。

if (res == ResultType.Failure)               
{
  something = ProcessFailure(..);
}
if (res == ResultType.ScheduledAndMonitored) 
{
  something = DoSomething(...) && DoSomething3(..); //Bit-and on the results? huh.
}
if (res == ResultType.MoreInfoAvailable)     
{
  info = GetInfo(..);
}
if (res == ResultType.OK && 
    someCondition)   
{
  something = DoSomething2(..);
}

但是,假设这是一个实际的片段,我会把它写成一个开关/案例的东西。可能会小心使用从前一个休息时间下降。 :○

答案 14 :(得分:1)

很难说你的样本中发生了什么,但看起来你做得很多。如果是这样的话,那么你想把这么多的逻辑塞进4行似乎是不对的。这是C#,而不是Python。说到C#vs Python,你没有使用if语句体的大括号,很多人都不喜欢。 StyleCop也不喜欢它。说到StyleCop,你也可能想要运行FxCop和ReSharper - 它可能会提出一种改进代码的好方法。你为什么不使用return?即使条件是互斥的,我发现return非常有用 - 允许跳过剩下的东西而不使用大量的else if,但仍然能够检测到所谓的“默认”情况(想想switch)。我刺伤了它:

// This should be in it's own function; I am just too lazy to spell out a signature.
{
    if (res == ResultType.Failure)
    {
        return ProcessFailure(..);
    }

    if (res == ResultType.ScheduledAndMonitored)
    {
        bool result = DoSomething(...);
        result = result && DoSomething3(..);
        return result;
    }

    if (res == ResultType.MoreInfoAvailable)
    { 
         info = GetInfo(..);
         // Ok, and then?
    }

    if (res == ResultType.OK && someCondition)
    { 
        return DoSomething2(..);
    }

    // Else ...

    Debug.Assert(false, "Why did we get here? Explanation: ");
}

我上面写的不是最好的代码,但我试图清理它。不要担心额外的变量 - 优化编译器会处理它们。请考虑逻辑可读性。我强烈认为,将事物放入表格格式并没有用。如果StyleCop很开心,代码太难阅读,那么你可能需要重新考虑它,以及简化逻辑/状态/等。

如果不确切知道您要做什么,很难提供更好的代码示例。

答案 15 :(得分:0)

创建语句块的大括号在整个行业范围内更为普遍,现在养成习惯,因为大多数人会认为如果你这样编写代码看起来很简单。

另一方面,我看到规则的原因之一是你总是使用花括号来表示if语句而永远不会使用if(条件)单行语句;是因为一行代码在4个月后更改并不常见,导致if语句在没有意识到的情况下进入下一个语句,因此作为安全,如果应该始终是:

if (condition)
{
    single-line-statement;
}

答案 16 :(得分:0)

考虑参数验证的情况以及函数中可能的早期退出。有时我喜欢格式化我的代码,类似于原始示例,以减少与其余代码相关的验证代码。我可以验证输入参数,或者我可以根据对象的内部状态检查hte请求操作的有效性。例如:

void LogMessage(LogLevel lvl, string msg)
{
  if (!this.loggingManager.IsLogging) return;
  if (lvl < this.loggingLevel) return;

  this.loggingManager.Write(this.loggerName, msg);
}

或者

void DrawGeometry(Graphics g, Geometry geom, Layer layer, double scale)
{
  if (geom == null) return;
  if (!layer.IsDisplayed) return;
  if (this.isDisplayByScale && (scale < this.minScale || scale > this.maxScale) return;
  if (this.ColorsToTreatAsTransparent.Contains(layer.Color)) return;

  Geometry xformed = this.Transform(geom);
  using (Pen p = new Pen(layer.Color, layer.Width))
  {
    g.Draw(xformed, p);
  }
}

请注意,这两个都是完全由示例组成的。我来回走动,但我有点像一线健全检查/退货声明的简洁。

答案 17 :(得分:0)

它对我们所有人来说都不是更具可读性。想象一下,如果普通的英文文本中有奇怪的表格来排列主语和句子的谓词。眼睛很难跟随,不是吗?我阅读的代码就像我读散文一样,所以当我不得不横向跳转时,这对我来说很紧张。但是,在初始化数据结构时使用选项卡很有帮助,因为这对我来说更符合逻辑。

这就是为什么代码格式标准很重要的原因;没有一种方法能够最好地格式化代码,让每个人都满意。