【问题标题】:Is object creation in getters bad practice?在吸气剂中创建对象是不好的做法吗?
【发布时间】:2011-01-07 06:46:42
【问题描述】:

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

或者我应该创建一个方法来创建和返回对象?

【问题讨论】:

    标签: c# oop getter


    【解决方案1】:

    是的,这是不好的做法。

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

    【讨论】:

    • 属性允许您返回任意值,例如,将对 Count 的调用传递给类中的变量(例如,返回 m_Collection.Count)
    • 验证设置器。例如特定范围。和懒惰的评估。
    • 它们也可以被声明为抽象的、虚拟的或者是接口的一部分。字段不能这样做。
    • @mitch 对不起,我误读了这个问题并投了反对票。当我写评论时,我意识到我是个白痴,然后去删除我的反对票。但猜猜怎么了?更改投票的窗口现在是 30 秒左右。因此,我必须编辑您的答案才能撤消我的错误。我的错,对不起,我是个笨蛋。
    • 当您在调试器中单步执行get 实现时,延迟加载可能是一个雷区。提示:关闭属性的调试器评估!
    【解决方案2】:

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

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

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

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

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

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

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

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

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

    来源

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

    Framework Design Guidelines”第 2 版,Brad Abrams,Krzysztof Cwalina,第 5.2 章属性设计

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 这意味着每次访问 getter 时都会得到不同的对象;肯定是个坏主意!
      • 嗯,当我读到这篇文章时,您似乎在说使用工厂方法模式是不好的做法。在我看来,这不是真的。但是,对 getter 和 setter 使用工厂方法模式是一种不好的做法。
      • IMO 工厂方法应命名为“createFoobar”,以明确创建新的 Foobar 对象。另一方面,在第一次调用时创建对象的延迟加载 getter 是 IMO 完全合法的。我希望 getter 在后续调用中返回相同的对象;延迟加载 getter 可以做到这一点。
      【解决方案4】:

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

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

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

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

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

      在字段的情况下,如下条件为真:

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

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

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

      【讨论】:

        【解决方案5】:

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

        改用包装类或类似的。

        【讨论】:

          【解决方案6】:

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

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

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

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

          【讨论】:

            【解决方案7】:

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

            它仍然应该代表一个关于对象的东西,不管值本身是如何得到的。例如,如果有问题的对象是一张发票,您可能需要将每个行项目的成本相加,然后返回总和。

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

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

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

            【讨论】:

              【解决方案8】:

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

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

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

              【讨论】:

                【解决方案9】:

                除了已经制作的 cmets,当通过属性延迟加载字段时,您可能会遇到一些真正的调试难题。

                我和

                一起上课
                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 调试器 正在实例化集合,因为它显示了类的公共属性。

                【讨论】:

                  【解决方案10】:

                  这对于structs 来说最多是可以接受的。对于引用类型,我只会在 getter 中使用一些延迟加载模式创建一个新对象。

                  【讨论】:

                    【解决方案11】:

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

                    【讨论】:

                      【解决方案12】:

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

                      【讨论】:

                        【解决方案13】:

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

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

                        【讨论】:

                          【解决方案14】:

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

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

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2019-11-26
                            • 1970-01-01
                            • 2018-11-16
                            • 1970-01-01
                            • 2018-04-05
                            相关资源
                            最近更新 更多