在getter中创建对象是不是很糟糕?

让我们在getter中创建一个像这样的对象:

public class Class1 { public string Id { get; set; } public string Oz { get; set; } public string Poznamka { get; set; } public Object object { get { // maybe some more code return new Object { Id = Id, poznamla = Poznamka, Oz = OZ }; } } } 

或者我应该创建一个将创建并返回对象的方法?

属性看起来像字段,但它们是方法。 众所周知,这会造成惊人的混乱。 当程序员看到似乎正在访问字段的代码时,程序员做出的许多假设可能不适用于某个属性。因此,有一些常见的属性设计指南。

  1. 避免从属性getter返回不同的值。 如果连续多次调用,则属性方法每次都可以返回不同的值; 每次返回相同的值。

  2. 属性方法可能需要额外的内存或返回对实际上不属于对象状态的内容的引用,因此修改返回的对象对原始对象没有影响; 查询字段始终返回对保证属于原始对象状态的对象的引用。 使用返回副本的属性可能会让开发人员感到非常困惑,而且这种特性通常没有记录。

  3. 考虑不能​​将属性作为out或ref参数传递给方法; 一个领域可以。

  4. 避免长期运行的财产吸气剂。 属性方法可能需要很长时间才能执行; 现场访问总是立即完成。

  5. 避免从getter中抛出exception。

  6. 如果属性设置器抛出exception,请保留以前的值

  7. 避免可观察到的副作用。

  8. 允许以任何顺序设置属性,即使这会导致对象的临时无效状态。

来源

“ 通过C#CLR ”,杰弗里里希特。 第9章智能定义属性

“ 框架设计指南 ”第2版,Brad Abrams,Krzysztof Cwalina,第5.2章财产设计

是的,这是不好的做法。

理想情况下,getter不应该改变或创建任何东西(除了延迟加载,即便如此,我认为它会导致代码不太清晰……)。 这样可以最大限度地降低无意识副作用的风险。

如果你希望你的getter在每次访问时都创建一个新对象,那就是这样做的方法。 此模式通常称为工厂方法 。

然而,这通常不需要在属性(即getter和setter)上,因此被认为是不好的做法。

是的,它是……从外面看,它应该是透明的,无论你是访问一个属性还是一个字段……

当你从田野或房产中读两次时,你会发现两件事:

  • 对象的(外部)行为没有影响
  • 你得到相同的结果

我对C#没有真正的了解,但我希望,以下内容使我的观点清楚。 让我们这样开始:

 Object o1 = myInst.object; Object o2 = myInst.object; o1.poznamka = "some note"; 

在字段的情况下,以下条件将成立:

 o1 == o2; o2.poznamka == "some note"; 

如果你使用带有getter的属性,每次调用都返回一个新对象,两个条件都将为false …

你的getter似乎是为了生成你的实例的临时快照…如果这是你想要做的,而不是使它成为一个简单的方法…它避免任何歧义……

就所有意图和目的而言,财产应该像一个领域。 这意味着不应抛出任何exception,并且不应创建新对象(因此,如果在循环中使用该属性,则不会创建大量不必要的对象)

请改用包装类或类似方法。

据我说,如果某些东西是’属性’,那么getter应该返回一个与该对象相关的属性(基本上是已经存在的数据)。

在您的情况下,您返回的东西当时不属于该对象的属性。 您没有返回对象的属性,而是返回某个操作的产品。

我会选择像GetMyObject()这样的方法。 特别是如果有’行动’将会发生,我认为大多数时候最好有一个方法而不是属性名称

并试着想象一下,在看到您的房产后,其他不熟悉您的代码的开发人员会期待什么。

属性只是表达计算字段的便捷方式。

无论价值本身是如何到达的,它仍应代表某个对象。 例如,如果相关对象是发票,则可能必须累计每个订单项的费用,并返回总计。

问题中写的内容违反了该规则,因为返回对象的副本不是描述对象的内容。 如果返回值在对属性的调用之间发生更改而未显式更改对象状态 ,则对象模型将被破坏。

一般来说,返回这样一个新对象几乎总会打破规则(我现在想不到反例),所以我会说这是不好的做法。

还有一些属性,您可以轻松无误地多次调用属性并最终运行相同的代码(希望不会很慢!)。

为了编写易于测试的代码,您必须保持对象初始化的分离。

即,在测试用例中,您没有持有测试某些特定项目。

就像在House对象中你不想测试与厨房对象有关的任何东西。 而你只测试花园。 因此,当您在某些构造函数或getter中启动一个house类并启动对象时,您将不会编写良好的代码来支持测试。

除了已经发表的评论之外,当通过属性延迟加载字段时,您可能会遇到一些真正的调试问题。

我上过课

 private Collection moo; public Collection Moo { get { if (this.moo == null) this.moo = new Collection(); return this.moo; } } 

然后在课堂的其他地方有一个引用的公共方法

 this.moo.Add(baa); 

没有检查它被实例化。

正如预期的那样,它抛出了一个空引用exception。 但是这个例外是在一个UI线程上,因此并不是很明显它来自哪里。 我开始追踪,每次我追查时,错误都消失了。

有一段时间我不得不承认我以为自己疯了。 调试器 – 没有错误。 运行时,错误。 稍后我发现了很多错误,我发现错误,并意识到Visual Studio调试器实例化了Collection,因为它显示了类的公共属性。

struct可能最多可以接受。 对于引用类型,我只会在使用一些延迟加载模式进行一次时在getter中创建一个新对象。

这取决于吸气剂的使用。 这是一个包含这种延迟加载代码的好地方。

这是一个不好的做法。 在您的示例中,每次访问object属性时都应该能够期望相同的Object

正如你所拥有的那样它很糟糕,但并不是类似于一种可接受的实践,称为延迟加载,可以在这里阅读。

http://www.aspcode.net/Lazy-loading-of-structures-in-C-howto-part-8.aspx

这是一个不好的做法。 但是,如果您将对象视为一堆getter和setter,那么您应该检查有关该主题的经典讨论。

正如一些人提到的,延迟加载可能是这样做的原因。 取决于您在此处建模的实际业务逻辑。 如果出于易读性目的更好,则应创建一个单独的方法,但如果创建对象的代码很简单,则可以避免间接。