25

让我们在 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 };
             }
        }
 }

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

4

14 回答 14

22

是的,这是不好的做法。

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

于 2010-01-20T13:35:34.403 回答
16

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

  1. 避免从属性 getter 返回不同的值。如果连续多次调用,一个属性方法可能每次返回不同的值;一个字段每次返回相同的值。

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

  3. 考虑到属性不能作为 out 或 ref 参数传递给方法;一个字段可以。

  4. 避免长时间运行的属性获取器。一个属性方法可能需要很长时间才能执行;现场访问总是立即完成。

  5. 避免从 getter 中抛出异常。

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

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

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

来源

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

框架设计指南”第 2 版,Brad Abrams,Krzysztof Cwalina,第 5.2 章属性设计

于 2010-01-20T18:04:20.293 回答
8

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

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

于 2010-01-20T13:37:09.767 回答
4

是的,它是......从外部来看,它应该是透明的,无论您访问属性还是字段......

从字段或属性读取两次时,您期望两件事:

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

我对 C# 没有真正的了解,但我希望下面的内容能说明我的观点。让我们这样开始:

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

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

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

如果您使用带有 getter 的属性,每次调用都会返回一个新对象,那么这两个条件都将为 false ...

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

于 2010-01-20T14:29:41.097 回答
2

就所有意图和目的而言,属性应该像一个字段一样工作。这意味着不应抛出异常,也不应创建新对象(因此,如果在循环中使用该属性,则不会创建大量不必要的对象)

改用包装类或类似的。

于 2010-01-20T13:38:00.547 回答
2

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

在您的情况下,您正在返回当时不是该对象属性的东西。您返回的不是对象的属性,而是某些操作的产物。

我会使用类似 GetMyObject() 的方法。特别是如果有一个“动作”会发生,我认为大多数时候最好有一个方法而不是一个属性名称

并尝试想象其他不熟悉您的代码的开发人员在看到您的属性后会期望什么。

于 2010-01-20T14:36:10.427 回答
1

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

无论值本身是如何获得的,它仍然应该代表有关对象的某些内容。例如,如果有问题的对象是发票,您可能必须将每个行项目的成本相加,然后返回总计。

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

笼统地说,像这样返回一个新对象几乎总是会违反规则(我现在想不出反例),所以我会说这是不好的做法。

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

于 2010-01-20T15:09:17.217 回答
1

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

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

就像在 House 对象中一样,您不想测试与厨房对象相关的任何内容。你只想测试花园。因此,当您在某些构造函数或 getter 中启动房屋类并启动对象时,您不会编写好支持测试的代码。

于 2010-01-20T19:14:24.547 回答
1

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

我有一个课

private Collection<int> moo;

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

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

this.moo.Add(baa);

没有检查它是否被实例化。

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

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

于 2010-01-21T16:14:29.570 回答
0

对于 s来说,这可能最多是可以接受struct的。对于引用类型,我只会在 getter 中创建一个新对象,因为它只使用一些延迟加载模式完成一次。

于 2010-01-20T13:37:35.620 回答
0

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

于 2010-01-20T13:37:51.870 回答
0

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

于 2010-01-20T13:38:21.870 回答
0

正如您所拥有的那样,它很糟糕,但与称为延迟加载的可接受做法不同,可以在此处阅读。

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

于 2010-01-20T13:39:10.120 回答
0

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

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

于 2010-01-20T13:41:58.793 回答