49

This hit me recently on a project I was working on. Most people are familiar with property recursion:

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

You accidentally put an upper-case T in this setter, and you've opened yourself up to a StackoverflowException. What's worse is if you've not defined it, often visual studio will auto-correct the casing for you to the invalid state.

I did something similar however in a constructor recently:

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

Unfortunately here you don't get a StackOverflowException, now you've got a programming error. In my case this value was passed to a WebService that instead used a default value (which wasn't 0) which caused me to miss the fact I had incorrectly assigned it. Integration tests all passed because this service didn't say

"Hey you forgot this really important field!"

What steps can I take to avoid this sort of behaviour? I've always been advised against defining variables like the following, and I don't like them personally, but I can't think of any other options:

private int _test;
private int mTest;

EDIT

Reasons that the underscore or m prefix are undesirable normally that I can think of are:

  • Readability
  • Slightly more difficult to scroll through members if you're inheriting from 3rd party classes as you get a mix of styles.
4

5 回答 5

64

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

public int Test { get; set; }

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

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

只需键入

private int test;

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

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

您无需费心单击上下文菜单。如果您喜欢键盘,快捷键是Ctrl+ R+ E

或者找一个 Resharper,它会立即指出你的愚蠢错误。

于 2013-10-30T13:13:02.487 回答
34

集成测试全部通过

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

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

您可以定义命名标准,甚至使用StyleCop 之类的工具来尝试强制执行这些标准。这可能会让你涵盖很多,尽管它不是一个铁定的解决方案,错误仍然可以通过。我个人同意你的观点,在代码中修饰变量名是难看的。也许在某些情况下这是一个有效的权衡?

最终,自动化测试是您对此类错误的防御。最简单的是,如果一个错误通过您的测试并进入生产,那么响应应该是:

  1. 编写一个测试来重现错误。
  2. 修复错误。
  3. 使用测试来验证修复。

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

于 2013-10-30T13:13:10.410 回答
6

使用代码片段。

对于私有字段支持的每个属性,请使用您创建的自定义代码片段,而不是从头开始编写它或让 IntelliSense 完成这项工作(效果不佳)。

毕竟,这个问题是关于约定和纪律,而不是语言设计。C# 的大小写敏感特性和 Visual Studio 中不完美的代码完成是我们犯这些错误的原因,而不是我们缺乏知识和设计。

你最好的选择是消除事故的可能性,并且有一个正确编写这些重复性内容的预定义方式是最好的方法。与记住约定并手动执行它们相比,它也更加自动化。

Visual Studio 中有一个默认的代码片段。键入propfull并点击 Tab,然后指定实例变量名称和属性名称,一切顺利。

于 2013-10-30T14:15:40.110 回答
3

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

于 2013-10-30T19:02:31.650 回答
2

你能不能只写一个测试来覆盖这个?

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

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

作为记录,如果我需要一个私有字段来存储公共 getter/setter 的值,我总是强调它。只是有一个强调隐私的下划线!

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

private string _test;
于 2013-11-06T09:45:03.533 回答