【问题标题】:Obfuscated C# Code - What is the balance between concision and clarity?混淆的 C# 代码 - 简洁和清晰之间的平衡是什么?
【发布时间】:2010-01-23 21:48:57
【问题描述】:

几年前曾经有过一场比赛,看谁能产生最模糊的 C 代码,其中一些结果非常难以理解。 C是这样的。你真的可以用预处理器搞砸事情。

但是,C# 的许多新功能为混淆代码提供了绝佳的机会。我想知道是否有人对在代码的简洁和清晰之间找到正确的平衡有意见。让我举一个例子来讨论,将项目填入ListView 的任务。 (是的,我知道您可以通过数据绑定来做到这一点,但请跟我来。)

控件是两列要填充的数组

struct Person
{
    public string name;
    public string address;
};

一个简单明了的方法是这样的:

private void Fill(Person[] people)
{
    foreach(Person person in people)
    {
        string[] columns = new string[2];
        columns[0] = person.name;
        columns[1] = person.address;            
        ListViewItem item = new ListViewItem(columns);
        listView1.items.Add(item);
    }
}

清晰易懂。

我也可以这样写:

private void Fill(Person[] people)
{
    foreach(Person person in people)
    {
        string[] columns = new string[] { person.name, person.address };
        ListViewItem item = new ListViewItem(columns);
        listView1.items.Add(item);
    }
}

甚至:

private void Fill(Person[] people)
{
    foreach(var person in people) // Note use implicit typing here
    {
        listView1.items.Add(new ListViewItem(
        new string[] { person.name, person.address }));
    }
}

最后,我也可以这样写:

private void Fill(Person[] people)
{
    Array.ForEach(people, item =>
    listView1.items.Add(new ListViewItem(
    new string[] { person.name, person.address}));
}

每个人都或多或少地使用了该语言的各种新功能。您如何在简洁和清晰之间找到平衡?我们应该每年举办一次混淆 C# 比赛吗?

【问题讨论】:

  • 不,但如果你想参加一场混淆的 perl 比赛,那可能会很有趣。
  • 和“函数式编程”“最佳实践”标签适用于什么?
  • 对不起,看不出有什么混淆,可能是我太习惯这种语言了
  • 哦,我现在明白了。你认为这是“函数式编程”,因为=> 是一个活页夹。你刚刚绑定了item,后来又使用了person
  • 使用 lambda 函数是函数式编程的精髓。也许我可以写更多的例子,但 C# 3.0 的功能特性是经常出现复杂性和简洁性的地方之一。最佳实践,因为我的问题不是关于混淆(尽管有标题),而是关于在简洁和简单之间找到平衡。

标签: readability


【解决方案1】:

你知道什么是困难的吗?编写其他人可以阅读和维护的代码。任何白痴都可以编写可编译且无法维护的代码。

始终支持可维护性:这就是您找到平衡点的方式。

编辑:

“任何傻瓜都可以编写计算机可以理解的代码。优秀的程序员编写人类可以理解的代码。”

  • Martin Fowler,重构:改进现有代码的设计

感谢roygbiv 找到上述报价。向福勒道歉,因为他谋杀了他的报价;我知道我以前读过它,只是不记得在哪里。

【讨论】:

  • 那么编写只有刚上大学的孩子才能理解的代码会更好吗?毕竟,他可能是维护您的代码的人。
  • 你在和十个蹩脚的程序员一起做一个项目吗?那么是的,我想你应该编写他们可以轻松理解的代码,但也有人可能会说你应该从事不同的项目。否则,可能不会。
  • @Fred Thomas:是的,差不多。我相信你和你的雇主都会因此而非常高兴。
【解决方案2】:

将所有内容塞入一行并不会使其“混淆”——它只会让您不必要地滚动很多内容。对于任何了解 C# 的人来说,理解您提供的任何示例仍然是微不足道的,而且如果您使用换行符,那么没有一个会比其他的更好或更差。

【讨论】:

  • 是的,OP提供的样本很难被称为混淆。
【解决方案3】:

代码具有最大的可读性,但是

  1. 请记住,多余的冗长和语法噪音会损害可读性。如果更简洁的符号允许您更直接地表达您的意图,则更简洁可以同时提高可读性。例如,将真实的 lambda 函数与使用单方法接口模拟它们进行比较。

  2. 假设阅读您的代码的其他人是体面的程序员并且了解您正在使用的语言。不要假设语言律师的知识水平,但假设具有良好的工作知识。不要以最小的公分母编写代码,因为虽然它可能使代码更容易被代码猴子维护,但它会惹恼你和真正知道自己在做什么的维护程序员。

更具体地说,示例 1 对于如此简单的事情来说有太多的语法噪音。示例 4 对人类来说非常难以解析。我会说 2 和 3 都很好,尽管在示例 3 的情况下我会重新格式化它,只是为了让人类更容易解析所有的函数调用嵌套:

private void Fill(Person[] people)
{
    foreach(var person in people)
    {
        listView1.items.Add(
            new ListViewItem(
                new string[] { person.name, person.address }
            )
         );
    }
}

现在您拥有两全其美:人类可以轻松解析它,并且没有任何多余的、不必要的冗长临时变量。

编辑:我也认为使用隐式变量类型,即var 在大多数情况下都可以。人们用动态语言编写完全可读的代码,其中隐式类型是唯一的类型,而且大多数时候,变量的形式类型是低级细节,与代码的高级意图几乎没有关系。

【讨论】:

    【解决方案4】:

    至少就您在此处提供的示例而言,我真的不认为混淆会随着您继续进行,直到您到达最后一个。即使在那里,任何模棱两可的唯一原因是 Lambda 的存在,这只是需要一些时间来适应。因此,新手可能会遇到最后一个问题,但不应该像旧的狂野 C 竞赛条目不可读那样发现其他不可读。

    不同之处在于这些 C# 示例都处于相同的抽象级别 - 更简洁的示例只是删除“绒毛”。在 C 中,由于 A) 任意重命名/别名构造和 B) 将多个级别的内存访问捆绑到一个语句中,您可能会产生歧义。

    整体而言,您可以用任何语言纠正晦涩难懂的代码,但我不认为 C# 像 C 那样容易出现这种情况,而且,事实上,我认为它是一种比许多语言更清晰的语言——即使使用一些更高级的构造。

    【讨论】:

    • 其实我不同意。不同之处在于,在前面的示例中,中间体被命名和识别。例如,在第一个示例中,我们有一个名为 columns 的变量,它告诉您字符串数组代表什么。然后你有一个中间项目(连同它的类型​​)告诉你它代表什么。然后在最后插入项目。也就是说,前面的示例将其分解为更小的步骤,而后面的示例将所有步骤组合在一起。有许多简单的部分更好还是有一个更复杂的部分更好?
    • 这在更大的作品中可能是正确的,但在这里,每个中间项目都是微不足道的;即“带有名称和地址的列表项。”将其绑定到名称“item”并没有真正增加太多价值。
    • 我必须同意 mquander 的观点 - 有一点是中间体是如此微不足道,以至于几乎更难通过所有名称。您想将所有人添加到列表中……您真的需要四五行中间声明和分配来做到这一点吗?我的书中没有。感谢您的评论...
    • 我想我本可以提供一个更好的例子,但是你可以用这种格式做些什么是有限制的。我是一个前 C++ 人,我只是注意到很多这些中间产品被内联插入(例如新的 string[] {...} 东西。我想这是垃圾收集器的后果之一,但是这类事情往往会出现在中间体中,我不确定所有内联是一种改进。感谢您的反馈。
    【解决方案5】:

    C# 和 VB.NET 语言的设计更加清晰,因为它们的操作级别比 C 更高。C 是可以说的接近金属的编程。编写像 C 那样的混淆 C# 是不可能的。

    “任何傻瓜都可以编写计算机可以理解的代码。优秀的程序员编写人类可以理解的代码。”

    • Martin Fowler,重构:改进现有代码的设计

    【讨论】:

      【解决方案6】:

      我没有发现这个例子被混淆了。

      首先,意图非常明确,新手更有可能了解 lambda 的功能,而不是不了解代码。这是使用更“高级”技术的理想场所 - 即使是不知道自己在做什么的人也能理解他们“应该”做什么。

      其次,以上所有内容不仅没有被混淆,而且是完全惯用的 C#。最后一个可以说不是那么多,因为 Array.ForEach 没有被广泛使用,大多数人(我曾与之合作过)都会使用 LINQ。

      【讨论】:

        猜你喜欢
        • 2010-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-22
        • 2016-02-17
        • 2019-08-12
        • 1970-01-01
        相关资源
        最近更新 更多