【问题标题】:To use a read-only property or a method?使用只读属性或方法?
【发布时间】:2015-05-19 20:13:00
【问题描述】:

我需要公开一个类实例的“被映射?”状态。结果由基本检查确定。它不是简单地暴露一个字段的值。我不确定是否应该使用只读属性或方法。

只读属性:

public bool IsMapped
{
    get
    {
        return MappedField != null;
    }
}

方法:

public bool IsMapped()
{
    return MappedField != null;
}

我已经阅读了 MSDN 的 Choosing Between Properties and Methods 但我仍然不确定。

【问题讨论】:

  • 我认为詹姆斯回答中的 sysexpands 评论就是您的回答。当您回顾您的代码或其他开发人员查看它时;将其作为属性几乎肯定表明它仅返回与字段相关的值。没有理智的开发者会把过多的功能放入财产权中?另一方面,10 个答案都说 RO 属性肯定是相同的答案
  • 那么阅读MappedField 意味着什么?它是对变量的简单读取,还是潜在的昂贵操作或具有副作用(例如延迟加载)的操作?

标签: c# .net class methods properties


【解决方案1】:

C# 标准说

§ 8.7.4

属性是提供对对象或类的特征的访问的成员。属性的示例包括字符串的长度、字体的大小、窗口的标题、客户的姓名等。属性是字段的自然扩展。两者都是具有关联类型的命名成员,访问字段和属性的语法相同。但是,与字段不同,属性不表示存储位置。相反,属性具有指定在读取或写入其值时要执行的语句的访问器。

虽然 as 方法被定义为

§ 8.7.3

方法是实现可由对象或类执行的计算或动作的成员。方法有一个(可能是空的)形式参数列表、一个返回值(除非方法的返回类型是 void ),并且可以是静态的或非静态的。

属性方法用于实现encapsulation。属性封装数据,方法封装逻辑。这就是为什么在公开数据时应该首选只读属性的原因。在您的情况下,没有修改对象内部状态的逻辑。您希望提供对对象特征的访问

您的对象IsMapped 的实例是否是您的对象的特征。它包含一个检查,但这就是你有属性可以访问它的原因。可以使用逻辑定义属性,但它们不应暴露逻辑。就像第一个引用中提到的示例一样:想象一下String.Length 属性。根据实现的不同,该属性可能会遍历字符串并计算字符数。它也确实执行了一项操作,但“从外部”它只是对对象的内部状态/特征进行了陈述。

【讨论】:

  • "您想提供对对象特征的访问。" - 这就是我要找的。这是具有适当 MSDN 参考的最完整答案。谢谢。
  • 虽然不是一个硬性规定,但当返回属性的计算时间很重要时,我也倾向于使用一种方法。属性应立即返回。
  • +1 明确关注语义而非形式标准
【解决方案2】:

我会使用该属性,因为没有真正的“做”(动作),没有副作用,也不太复杂。

【讨论】:

  • 我希望编码人员尽可能频繁地说“我不使用 ***,因为没有参数,我为什么需要它”。
【解决方案3】:

我个人认为method 应该做某事或执行某些操作。您没有在 IsMapped 内执行任何操作,因此它应该是 property

【讨论】:

    【解决方案4】:

    我会去买房。主要是因为引用的 MSDN 文章的第一句话:

    一般来说,方法代表动作,属性代表数据。

    【讨论】:

      【解决方案5】:

      在这种情况下,我似乎很清楚它应该是一个属性。这是一个简单的检查,没有逻辑,没有副作用,没有性能影响。没有比检查更简单的了。

      编辑:

      请注意,如果有上面提到的任何一个,并且你会将它放入一个方法中,那么该方法应该包括一个强动词,而不是像 is 或 has 这样的助动词。方法某事。您可以将其命名为 VerifyMapping 或DetermineMappingExistance 或其他名称,只要它以动词开头即可。

      【讨论】:

      • MappedField != null 定义为逻辑语句。
      • 您在这里假设阅读MappedField 简单、快速且没有副作用。我们不知道这是真的。
      • @MichaelKjörling 显然,他可以实现一个自定义操作符 != 来格式化他的硬盘,但是你可以通过多种方式破坏所有东西。只要没有指定任何内容,我都会假设我看到的内容是符合标准的。
      【解决方案6】:

      我认为您链接中的这一行是答案

      方法代表动作,属性代表数据。

      这里没有动作,只是一条数据。所以它是一个属性。

      【讨论】:

      • 它做了一个计数...(一个动作)
      • 但我也在做一个动作……null检查
      • 您正在检查一条数据,而不是对其执行操作。 - 如果基于结果你做了其他事情或执行计算或其他事情,那么这将是一个方法。
      • IEnumerable.Count() 是一种扩展方法,它基本上遍历集合并计数包含元素。另一方面,IList.Count 是一个属性,因为它不遍历列表 - 列表已经知道它的计数并简单地从属性中返回它。
      • IEnumerable.Count() 不存在。 Count() 是一种扩展方法,可在静态类 Enumerable 中找到,1) 扩展属性不存在,2) Count() 可能很耗时(尤其是在复杂的 LINQ 查询中)。
      【解决方案7】:

      在您可以使用这两种结构的情况/语言中,一般划分如下:

      • 如果请求是针对对象拥有的东西,请使用属性(或字段)。
      • 如果请求是针对对象所做的某事的结果,请使用方法。

      更具体地说,属性将用于以读取和/或写入方式访问(用于消费目的)由公开属性的对象拥有的数据成员。属性优于字段,因为数据不必一直以持久形式存在(它们允许您“懒惰”地计算或检索此数据值),并且它们优于用于此目的的方法,因为您仍然可以在代码中使用它们,就好像它们是公共字段一样。

      然而,属性不应该产生副作用(设置一个变量以保持返回的值,避免昂贵的多次重新计算所需的值);在所有其他条件相同的情况下,它们应该返回确定性结果(因此 NextRandomNumber 对于属性来说是一个糟糕的概念选择),并且计算不应导致任何会影响其他计算的状态数据的更改(例如,获取 PropertyA 和按该顺序获取 PropertyB 不应返回与获取 PropertyB 然后获取 PropertyA 不同的结果。

      方法OTOH在概念上被理解为执行一些操作并返回结果;简而言之,它做了一些事情,可能超出了计算返回值的范围。因此,当返回值的操作具有额外的副作用时,将使用方法。返回值可能仍然是某些计算的结果,但该方法可能已对其进行了非确定性计算(GetNextRandomNumber()),或者返回的数据是对象的唯一实例的形式,再次调用该方法会产生一个不同的实例,即使它可能具有相同的数据 (GetCurrentStatus()),或者该方法可能会更改状态数据,从而连续两次执行完全相同的操作会产生不同的结果 (EncryptDataBlock();许多加密密码通过这种方式工作设计以确保连续两次加密相同的数据会产生不同的密文)。

      【讨论】:

        【解决方案8】:

        如果在任何时候您都需要添加参数以获取值,那么您需要一个方法。否则你需要一个属性

        【讨论】:

        • 有了这个定义,索引属性呢?
        • 如果该值在对象中占主要部分,那么可以。
        【解决方案9】:

        恕我直言,第一个只读属性是正确的,因为 IsMapped 作为对象的属性,并且您没有执行操作(仅评估),但最终与现有代码库的一致性可能很重要不仅仅是语义......除非这是一个单一的分配

        【讨论】:

          【解决方案10】:

          我同意这里的人的说法,因为它是获取数据,并且没有副作用,所以它应该是一个属性。

          为了扩展这一点,如果副作用对“从外部查看”的人有意义,我也会接受一些带有 setter(但不是 getter)的副作用。

          一种思考方式是,方法是动词,属性是形容词(同时,对象本身是名词,静态对象是抽象名词)。

          动词/形容词准则的唯一例外是,在获取(或设置)相关信息时使用方法而不是属性可能会非常昂贵:从逻辑上讲,这样的功能可能仍然应该是一个属性,但人们习惯于将属性视为对性能的影响较小,虽然没有真正的理由为什么总是如此,但强调 GetIsMapped() 在性能方面相对较重可能会很有用事实上是。

          在运行代码的层面上,调用属性和调用等效方法来获取或设置绝对没有区别;这一切都是为了让编写代码的人更轻松地使用它。

          【讨论】:

            【解决方案11】:

            我希望属性,因为它只返回字段的详细信息。另一方面,我希望

            MappedFields[] mf;
            public bool IsMapped()
            {
                 mf.All(x => x != null);
            }
            

            【讨论】:

            • 在本例中,您正在执行多个null 检查。这与我的逻辑上有何不同,我也在执行null 检查?
            • 不同的是,在您查询 mf 的每个成员之前,您不知道答案
            【解决方案12】:

            你应该使用这个属性,因为 c# 有这个原因的属性

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-02-22
              • 2018-10-17
              • 1970-01-01
              • 2013-05-20
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多