【问题标题】:Using a lot of hardcoded strings in code在代码中使用大量硬编码字符串
【发布时间】:2012-02-29 15:29:04
【问题描述】:

当我查看我的代码并编写类似...的东西时。

if (role == "Customer")
{
    bCustomer = true;
}
else if (role == "Branch")
{
    bIsBranch = true;
}

或者

foreach(DataRow as row in myDataSet.Tables[0].Rows)
{
    row["someField"]=somefield.Tostring()
}

你们在做这个吗?什么时候可以这样做,什么时候不应该这样做?如果有的话,写这篇文章的更好方法是什么?

感谢您的评论:我想我应该添加如果(出于此示例目的)我只使用此角色比较一次?开设一个全新的班级仍然是一个更好的主意吗?我还应该有 1 个名为“常量”的类是多个包含特定常量的类,例如“角色”类吗?

【问题讨论】:

  • 很难给出明确的答案,但在第一个示例中,如果适用,我会尝试对其进行重构以使用多态性。在第二种情况下,我可能会在 DataTable 周围创建一个包装类,该类将列名作为字符串常量,因此它们有一个中心位置。所有消费者都只使用包装类而不是 DataTable。
  • 这些天使用nameof来指代名称代码元素,如typesclassesproperties等。有关nameof的更详细了解,请参阅下面的回答表达。

标签: c#


【解决方案1】:

没有。不要使用“magic strings”。而是创建一个带有常量的静态类,或者如果可以的话,创建一个枚举。

例如:

public static class Roles
{
  public const string Customer = "Customer";
  public const string Branch = "Branch";
}

用法:

if (role == Roles.Customer)
{

}
else if (role == Roles.Branch)
{

}

这是good discussion on various solutions

【讨论】:

  • 我不明白为什么这是“魔术字符串”的一个实例?来自维基:“魔术字符串是程序员认为永远不会来自外部并激活其他隐藏功能的输入。该程序的用户可能会提供在大多数情况下给出预期响应的输入。但是,如果用户这样做事实上,无辜地提供了预定义的输入,调用了内部功能,程序的响应往往出乎用户的意料(因此显得‘神奇’)”。
  • 我不喜欢维基百科文章中的措辞,但这是我找到的第一个定义。在软件中,魔术字符串通常是指开发人员很容易输入错误的字符串值。
  • 我通常认为“魔术字符串”是意义或用途是神秘的或不言自明的标志。如果不是编译器,整个 C# 语言将符合您的定义,更不用说所有解释型动态语言了。我总是打错那些东西……实际上,当我在没有参考的情况下记住 api 时,它确实有点像魔术。
  • 这与语言无关,只是在源代码中硬编码字符串值。作为一种静态类型的编译语言,任何关键字或类型的拼写错误都会被编译器捕获。
  • 在 C#(或 .NET 和许多其他语言 b.t.w.)中,使用 plublic const 变量被认为是代码异味。当您使用const 变量时,您需要确保它们不能离开程序集的范围(参见hereofficial documentation from Microsoft 中的注释)。而是将 const 设为内部变量或使用 public static readonly 变量。
【解决方案2】:

最好将硬编码的字符串单独声明为常量,而不是每次都声明一个新字符串。它保持代码干净,减少因输入错误引起的错误。

关于应该或不应该做完全取决于场景。

【讨论】:

    【解决方案3】:

    我会创建一个 Roles 静态类:

    public sealed class Roles
    {
        public const string BRANCH = "Branch";
        public const string CUSTOMER = "Customer";
    
        public static bool IsCustomer(string role)
        {
            return role == CUSTOMER;
        }
    }
    

    然后在你的代码中:

    bCustomer = Roles.IsCustomer(role);
    

    或者,这需要更多设置,但RoleProvder(取决于 Web 与否)提供了很多好的方法。

    【讨论】:

      【解决方案4】:

      我相信更好的方法是使用application settings,这意味着如果“客户”或“分支”值发生变化,您将无需重新编译代码。魔法值显然很糟糕,这将是摆脱它们的一个很好的第一步/选择。此外,它将您的值保存在一个地方,而且我也相信您可以在运行时 reload the settings 而无需重新启动应用程序(尽管我自己没有尝试过)。

      例如:

      if (role == Properties.Settings.Default.CustomerRole) 
      {     
          bCustomer = true; 
      } 
      else if (role == Properties.Settings.Default.BranchRole) 
      {    
          bIsBranch = true; 
      } 
      

      【讨论】:

      • 你能在应用程序设置中显示一些代码吗?
      • 在 Visual Studio 中,右键单击您的项目,选择“属性”->“设置”选项卡。然后单击以创建默认设置文件(如果不存在)。如果是这样,那么在此处添加您的条目​​,因为 CustomerRole 是一个值为“Customer”等的字符串。然后您可以通过静态默认实例引用它们,如我在示例中所示。查看此视频的第一部分:youtube.com/watch?v=t9WIvYQ1dNU 以及 3:50 分钟。
      【解决方案5】:

      多态性是一回事,但在代码中使用硬编码字符串一点都不好。最好定义一个保存字符串的变量并在代码中使用这个变量。这种情况下,如果你需要改变一些东西(相信我你会的),你只需改变这个变量的值就可以了(错误也更少!)

      【讨论】:

        【解决方案6】:

        为了可维护性,您应该尽可能将字符串比较器形式化,作为命名常量或枚举。程序员的好处是您可以本地化更改。即使使用重构工具,查找使用字符串的所有位置也可能很乏味且容易出错。您今天可能只有一个地方进行此比较,但您或未来的维护者可能会将其扩展到代码的其他部分。此外,类本身可能会增长并且需要被拆分。随着时间的推移,这些事情往往会逐渐出现在程序中。

        我会简单地将这些字符串声明为靠近使用它们的位置的常量,但在一起。在您知道自己需要之前,不要为角色之类的新抽象而烦恼。如果您需要比较的角色数量增加,或者需要在此类之外进行比较,那么您可以创建一个 Roles 枚举或 Roles 类,具体取决于比较的复杂性。

        此外,通过使用常量,您可以向编译器发出预期用途的信号,因此您可以获得一些较小的内存管理优势,鉴于您的比较是在循环中,这通常是一个好习惯。

        【讨论】:

          【解决方案7】:

          通过使用 nameof 表达式消除在 C# 代码中使用魔术字符串

          使用nameofexpression,您可以检索typesclassesstructsproperties、@的文字大小写 987654330@、functionsfieldsargumentsparameterslocals,以及更多它们在编译时出现在代码中的大小写。这不会消除或解决你所有的“魔术字符串”问题,但它是一个好的开始,值得讨论。

          例如,获取enum 值的文字大小写

          public enum ExportType
          {
             CSV,
             Excel
          }
          

          nameof用法

          nameof(ExportType.CSV); // "CSV"
          nameof(ExportType.Excel); // "Excel"
          nameof(ExportType); // "ExportType"
          

          返回参数中表达式的文字大小写

          不再有“魔术字符串”

          如果您指的是特定的type namesclassesetc. 的代号,强烈考虑将那些脆弱的魔术字符串替换为nameof。您不必担心更改内部类型或属性的名称,而不必担心破坏代码。使用 Visual Studio 等 IDE 的重命名功能将重命名代码库中引用该表达式的所有引用。

          类型安全

          此操作在编译时完成。最后,如果您在代码中依赖 typesclassesetc. 的名称,则可以在以下情况下将编译时类型安全引入您的逻辑指他们。

          性能

          您也可以消除代码中的大量反射,从而获得这些东西的名称

          注意事项

          获取泛型类型的名称

          nameof(T); // "T"
          nameof(TEntity); // "TEntity"
          

          在这些情况下,您必须在运行时继续使用反射来获取类型的名称。 nameof 不幸的是不是很有用。

          例如:

          var enumValuesNames = typeof(ExportType).GetProperties().Select(p => p.Name).ToArray();
          

          【讨论】:

            【解决方案8】:

            嗯,在我看来,这取决于您,这取决于您的应用程序设计。 我通常会从积极的方面来看待它——如果应用程序按照它应该工作的方式工作,那一切都很好。恕我直言

            【讨论】:

            • 工作是一回事,但是魔术字符串会带来一些其他问题,例如:难以维护:1)如果您的数据库或数据层发生变化,那么您需要在许多潜在位置更改魔术字符串; 2)你可以很容易地打错字。等
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-08-28
            • 1970-01-01
            • 2021-03-14
            • 1970-01-01
            • 1970-01-01
            • 2019-09-22
            相关资源
            最近更新 更多