避免属性递归

这最近在我正在进行的一个项目中给我带来了打击。 大多数人都熟悉财产递归:

public int Test 
{
   get { return this.test; }
   set { this.Test = value; }
}
private int test;

您不小心将一个大写的T放入了此设置器中,并打开了一个StackoverflowException的密码。更糟糕的是,如果您没有定义它,Visual Studio经常会自动将外壳改正为无效状态。

我最近在构造函数中做了类似的事情:

public TestClass(int test)
{
    this.Test = Test;
}

不幸的是,在这里您没有StackOverflowException,现在您遇到了编程错误。 在我的情况下,此值传递给WebService,而该WebService却使用默认值(不是0),这导致我错过了错误分配它的事实。 集成测试全部通过,因为此服务未说明

“嘿,你忘记了这个非常重要的领域!”

我应该采取什么步骤来避免这种行为? 始终建议我不要定义以下变量,并且我个人不喜欢它们,但是我无法想到其他任何选择:

private int _test;
private int mTest;

编辑

我想到的下划线或m前缀通常是不可取的原因是:

  • 可读性
  • 如果您要继承3rd Party类的样式,则在成员之间滚动会稍微困难一些。
Ian asked 2020-02-13T23:42:36Z
5个解决方案
62 votes

最好的方法是在此处使用“自动实现的属性”。

public int Test { get; set; }

如果由于某种原因无法使用“自动实现的属性”,请使用_前缀(不过我不喜欢)。

如果您也不想使用某些前缀,那么可以选择其他方法。 您不必手动编写属性代码。 让IDE为您完成; 这样,您可以避免粗心的错误。 (我不知道我是如何在原始答案中错过这个的)

只需输入

private int test;

选择字段,右键单击“重构”->“封装字段”。 IDE将为您生成属性片段,如下所示。

public int Test
{
    get { return test; }
    set { test = value; }
}

您无需打扰上下文菜单。 如果您喜欢键盘,快捷方式是Ctrl + R +E。

或得到一个Resharper,它将立即指出您的愚蠢错误。

Sriram Sakthivel answered 2020-02-13T23:43:16Z
33 votes

集成测试全部通过

然后他们还没有进行详尽的测试。 如果测试没有发现错误,那么您需要编写另一个测试。

那确实是这里唯一的自动化解决方案。 编译器不会抱怨,因为代码在结构和语法上都是正确的。 在运行时,这在逻辑上是不正确的。

您可以定义命名标准,甚至可以使用StyleCop之类的工具来尝试实施这些标准。 尽管这不是一个坚定的解决方案,但错误仍然可以解决,这可能会让您涵盖很多内容。 就我个人而言,我同意您的看法,即在代码中修饰变量名并不美观。 也许在某些情况下这是一个有效的权衡?

最终,自动化测试是您防范此类错误的防御措施。 简单来说,如果错误通过您的测试并进入生产阶段,则响应应为:

  1. 编写测试以重现该错误。
  2. 解决错误。
  3. 使用测试来验证修复程序。

当然,这仅涉及一种情况,而不涉及代码中的每个属性定义。 但是,如果这种情况经常发生,那么您可能会遇到人员问题而不是技术问题。 团队中有些人马虎。 解决该问题的方法可能不是技术性的。

David answered 2020-02-13T23:44:13Z
6 votes

使用代码段。

对于由私有字段支持的每个属性,请使用您创建的自定义代码段,而不是从头开始编写或让IntelliSense来做(差)。

毕竟,这个问题是关于惯例和纪律,而不是语言设计。 C#区分大小写的性质和Visual Studio中代码执行不完善的原因是我们犯这些错误的原因,而不是我们缺乏知识和设计。

您最好的选择是消除事故的机会,最好的方法是采用正确的方式编写这些重复的内容。 与记住约定并手动执行约定相比,它也更加自动化。

为此,Visual Studio中有一个默认代码段。 键入propfull并点击Tab,然后指定实例变量名称和属性名称,您就可以开始了。

Slavo answered 2020-02-13T23:44:52Z
3 votes

在某些情况下,您无法绕过setter和getter。 但是,如果您遵循“告诉,不要问”的原则,也许您不需要设置者和获取者? 它基本上说,更喜欢让具有数据的对象来工作,而不是让其他一些对象从数据对象中查询很多东西,做出决定,然后将数据写回到数据对象中。 参见[http://martinfowler.com/bliki/TellDontAsk.html]

Robert answered 2020-02-13T23:45:13Z
2 votes

您能不能只编写一个测试来涵盖这一点?

int constructorValue = 4;
TestClass test = new TestClass(constructorValue);
Assert.Equals(test.Test, constructorValue);

您可能不想立即编写测试来掩盖自己免受将来的困扰,但是您发现了一个错误,为什么不再次保护自己呢?

作为记录,如果我需要一个私有字段来存储公用获取程序/设置程序的值,我总是在其下划线。 有一个强调隐私的下划线!

public string Test
{
    get { return _test; }
    set { _test = value; }
}

private string _test;
Phil Cooper answered 2020-02-13T23:45:42Z
translate from https://stackoverflow.com:/questions/19682999/how-to-avoid-property-recursion