使用只读属性还是方法?

77

我需要暴露类实例的"is mapped?"状态。结果通过基本检查确定,这不仅仅是暴露字段的值。我不确定是否应该使用只读属性还是方法。

只读属性:

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

方法:

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

我已经阅读了MSDN的选择属性和方法之间,但我仍然不确定。


1
我认为James的回答中sysexpands注释就是你要找的答案。当你回顾自己的代码或其他开发人员查看它时,将其作为属性几乎肯定表明它只返回与字段相关的值。没有理智的开发人员会在属性中放入过多的功能,对吧?另一方面,10个回答都说RO属性肯定是相同的答案。 - Sayse
2
那么读取MappedField意味着什么?它是一个简单的变量读取,还是一个可能昂贵的操作或具有副作用(例如延迟加载)的操作? - user
12个回答

93

C#标准规定:

§ 8.7.4

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

而方法的定义如下:

§ 8.7.3

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

属性方法用于实现封装。属性封装数据,方法封装逻辑。这就是为什么如果你正在公开数据,应该首选只读属性。在你的情况下,没有逻辑修改对象的内部状态。你想要提供对对象特征的访问。

你的对象实例是否映射是你对象的一个特征。它包含一个检查,但这就是你使用属性访问它的原因。属性可以使用逻辑定义,但不应公开逻辑。就像在第一段引文中提到的例子:想象一下String.Length属性。根据实现方式,它可能需要遍历字符串并计算字符数。它执行了一个操作,但“从外部”看,它只是表达了对象内部状态/特征的陈述。


4
"你希望提供对对象某一特性的访问。" - 这就是我所寻找的。这是最完整的答案,并带有适当的MSDN参考资料。谢谢。" - Dave New
3
虽然这并非一条强制性的规则,但在计算属性返回时间较长时,我倾向于使用一个方法。属性应该立即返回。 - Andrew Hanlon
3
对于明确关注语义而非形式标准的做法表示赞同。 - Vlad

23

我会使用这个属性,因为它没有真正的“执行”(动作),没有副作用,并且不太复杂。


1
我希望程序员们尽可能经常地说出这样的话:“我不使用***,因为没有理由,我为什么需要它”。 - Nakilon

14

我个人认为一个方法应该执行某些操作或者行为。由于你在IsMapped中没有执行任何操作,所以它应该是一个属性


7

我会选择属性。主要是因为在MSDN文章中引用的第一句话:

通常,方法代表操作,属性代表数据。


5
在这种情况下,我认为它应该是一个属性。这是一个简单的检查,没有逻辑,没有副作用,没有性能影响。检查不会比这更简单了。
编辑:请注意,如果上述任何内容中有任何东西,并且您将其放入方法中,该方法应包括一个强动词,而不是像is或has这样的助动词。方法会做些什么。您可以将其命名为VerifyMapping、DetermineMappingExistance或其他任何以动词开头的名称。

但是,“MappedField!=null”在定义上是一个逻辑语句。 - Dave New
您在这里假设读取 MappedField 是简单、快速且没有副作用的。但我们并不知道这是真实情况。 - user
4
@MichaelKjörling 很明显,他可以实现一个自定义的不等于(!=)运算符来格式化他的硬盘,但是你可以用许多方式破坏所有东西。 只要没有指定任何内容,我就会认为我看到的是符合标准的。 - nvoigt

5
我认为你链接中的这一行就是答案:

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

这里没有动作,只有一段数据。因此它是一个属性。

3
它进行了计数...(一种动作) - Lotok
2
但我也在执行一个操作... 进行了一个 null 检查。 - Dave New
1
你正在检查一段数据,而不是对其执行操作。- 如果基于结果你做了其他事情或执行了计算或其他操作,那么它就是一个方法。 - Lotok
9
IEnumerable.Count() 是一个扩展方法,基本上通过遍历集合来计算元素的数量。另一方面,IList.Count是一个属性,因为它不遍历列表 - 列表已经知道它的数量,并且只需从属性中返回它。 - Zoran Horvat
5
IEnumerable.Count() 不存在。Count()是一个扩展方法,定义在静态类Enumerable中。1)扩展属性不存在,2)Count()很可能会消耗时间(尤其是在复杂的LINQ查询中)。 - Dave Van den Eynde
显示剩余2条评论

4
在有这两种构造的情况/语言中,一般的区分如下:
- 如果请求是关于对象“拥有”的东西,使用属性(或字段)。 - 如果请求是关于对象“做”的结果,使用方法。
更具体地说,属性用于以读取和/或写入方式访问数据成员,该数据成员(供消费目的)由公开属性的对象拥有。属性比字段更好,因为数据不必始终以持久形式存在(它们允许您对计算或检索此数据值进行“懒惰”),并且对于此目的,它们比方法更好,因为您仍然可以在代码中使用它们,就像它们是公共字段一样。
然而,属性不应导致副作用(除了可能可以理解的例外,即设置变量以保留返回的值,避免需要多次昂贵的重新计算值);所有其他事项相等时,它们应返回确定性结果(因此,NextRandomNumber对于属性来说是一个不好的概念选择),并且计算不应导致更改任何状态数据,这会影响其他计算(例如,按顺序获取PropertyA和PropertyB不应返回任何不同的结果,而按顺序获取PropertyB然后PropertyA)。
另一方面,方法在概念上被理解为执行某些操作并返回结果;简而言之,它做某些事情,这可能超出了计算返回值的范围。因此,当具有附加副作用的操作返回值时,应使用方法。返回值仍然可以是某些计算的结果,但该方法可能已经以非确定性方式计算了它(GetNextRandomNumber()),或者返回的数据以对象的唯一实例形式存在,即使它可能具有相同的数据(GetCurrentStatus()),或者该方法可能会更改状态数据,使得连续两次完全相同的操作产生不同的结果(EncryptDataBlock();许多加密密码按设计方式工作,以确保连续两次加密相同的数据产生不同的密文)。

3

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


3
按照这个定义,那么索引属性呢? - Mr47
如果该值在对象中占据了主要部分,那么是的,我们可能会这样做。 - Odys

2

我认为第一个只读属性是正确的,因为IsMapped作为对象的一个属性,你并没有执行动作(只是评估),但在一天结束时,与现有代码库的一致性可能比语义更重要....除非这是一个大学作业


2
我同意其他人的观点,因为它只是获取数据且没有副作用,所以应该将其作为属性。
此外,如果setter有一些“从外部看起来”很合理的副作用,我也会接受,但getter不行。
可以这样想,方法是动词,属性是形容词(同时,对象本身是名词,静态对象是抽象名词)。
唯一违反动词/形容词指南的例外情况是:当获取(或设置)相关信息非常昂贵时,使用方法而不是属性可能更合适:从逻辑上讲,这种特性可能仍然应该是一个属性,但人们习惯认为属性在性能上影响较小,虽然并非总是如此,但如果实际上GetIsMapped()确实很耗费资源,突出显示它的相对性能较差可能会有用。
在运行代码的级别上,调用属性和调用等效方法来获取或设置之间没有任何区别;这一切都是为了使编写使用它的代码的人生活更轻松。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接