为啥我们使用 .NET 属性而不是普通的旧 get/set 函数?

Posted

技术标签:

【中文标题】为啥我们使用 .NET 属性而不是普通的旧 get/set 函数?【英文标题】:Why do we use .NET properties instead of plain old get/set functions?为什么我们使用 .NET 属性而不是普通的旧 get/set 函数? 【发布时间】:2010-11-04 09:50:20 【问题描述】:

我了解提供间接访问类成员的接口的诸多好处。我的问题是:这不是你可以用任何 OO 语言使用(类似的东西)完成的事情

public int NormalClass::getQuality() 
    return this->quality;

protected void NormalClass::setQuality(int q) 
    this->quality = q;

?

.NET 属性除了美观之外还有哪些额外的好处?

如果您能提出令人信服的论据,我将接受“可读性”;但就我个人而言,我倾向于认为 get/set 函数比属性更具可读性,因为它明确地是 函数 而不是直接值。

编辑:感谢大家的回答!这对我来说真的很有帮助;总结一下我从所有所说的中收集/学到的东西,以下是我到目前为止得出的一些结论:

属性的最大好处 并非来自特定的特征 属性本身,而是 从框架和 IDE 功能 以特殊方式处理属性; 例如,属性编辑器、XML 序列化,数据绑定。 属性可以被视为简单 以某些方便的方式取值 get/set 函数不能:在 特别是 obj.Prop++ 和 obj.Prop = 价值。 属性让你逍遥法外 使用快速而肮脏的代码 公众成员无需通过 实现一堆的头痛 稍后获取/设置功能;如果你 应该需要添加一些逻辑 和/或将公共成员设为私有, 你可以简单地介绍一个属性 并且不要冒险破坏任何旧代码。

现在,到目前为止,在 2 或 3 个答案中提出了一点,我个人觉得有些怀疑:属性意味着廉价的读/写操作,因此可以以与简单变量基本相同的方式使用。我对这一点的问题是,属性中没有任何固有的东西可以真正强制执行这一点。这只是应该如何使用它们。对我来说,这类似于“shouldBePrivate”限定符,它指示值应该只能由其自己的类直接访问,但无论如何仍可以从外部访问;或者警察在街上巡逻以提醒我们应该表现自己,但在我们开始犯罪时实际上并不干预(如果不强制执行,它对我们有什么真正的作用? )。

如果属性有某种内置机制来确保读取/写入的廉价性,我会对这一点印象深刻。

【问题讨论】:

...就因为可以,呵呵 “如果属性有某种内置机制来确保读/写便宜” - 这有点像说“如果 [你最喜欢的 GUI 框架] 有某种内置机制会更好-in 机制确保人们不会编写糟糕的 UI”。唉,唯一的机制被称为“程序员”。 (或设计师,或可用性研究,或其他。)编译器可以帮助你,但你必须承担一些责任,不要写出糟糕的代码。 Joe,我认为这基本上是我的观点:使用属性本身不能保证廉价的读/写。这是程序员的责任。我并不是在争论框架拥有一个标准的、认可的方法来实现快速访问属性是一个好主意。相反,我质疑那些将属性的这种事实上的特性作为功能优势的答案,就好像它是 .NET 开发人员努力提供的一些特殊特性一样。 【参考方案1】:

Jon Skeet 在他的 C# 文章博客上对 why properties matter 进行了出色的概述。在其中他解释了为什么应该使用属性而不是暴露公共字段。

至于为什么要使用属性而不是getter/setter方法,我建议如下:

属性提供更简洁、更简洁的语法,易于理解和阅读。 属性启用赋值表达式链接:A.x = B.y = C.z 属性清晰一致地传达数据访问的语义 - 消费者期望没有副作用。 .NET 中的许多库都可以识别属性,用于执行 XML 序列化、WPF 绑定、ASP.NET 2 路绑定等任务。 属性被 IDE 和许多可视化设计器识别,并且可以在属性编辑器中显示。 属性支持递增 (++) 和递减 (--) 运算符。 属性可以很容易地与使用反射的方法区分开来,并允许动态消费者提取有关对象公开的数据的知识。 C# 3 支持有助于消除样板代码的自动属性。

【讨论】:

我认为这是最全面的答案。干得好。 不只是 ++ 和 --,还有像 +=、*=、|= 等修改赋值运算符。 哇,即使 Jon Skeet 没有回答问题,Jon Skeet 也在回答问题:)【参考方案2】:

对于getset 方法,您必须从一开始就决定使用它们,并且几乎总是为您的类公开的每个公共属性编写大量样板代码。

class Point

    private int x, y;

    // Ew, pointless boilerplate!
    public int  getX()       return x;   
    public void setX(int x)  this.x = x; 

    public int  getY()       return y;   
    public void setY(int y)  this.y = y; 


// ...

Point p = new Point();
p.setX(5);
p.setY(10);

使用属性,您可以消除样板 用于 90% 的只有微不足道的 getter 和 setter 的属性。您可以直接公开公共变量

class Point

    public int x, y;


Point p = new Point();
p.x = 5;
p.y = 10;

然后,如果您决定要向公共变量添加一些行为,您可以在 getset 方法中将它们切换为具有实际行为的属性。这里的好处是您班级的用户根本不会受到影响。什么都没有改变;他们不必从point.x = 5 切换到point.setX(5)。您的公共接口是稳定的,允许您首先使用普通变量,然后当您添加一些保护/记录/任何内容时切换到较慢的get/set 方法。

class Point

    public int x  get; set; 


// No change! 
Point p = new Point();
p.x = 5;
p.y = 10;

(现在严格来说,你的语法接口没有改变,但是你的类的编译接口已经改变了,所以如果你从变量切换到属性,你必须重新编译所有使用你的类的代码。你不能得到比如说,如果你的类是一个广泛使用的库的一部分,那么只需重新编译你的类并删除它来代替旧类。你的库的用户将不得不针对你的库的新版本重新编译他们的代码。)

【讨论】:

我非常喜欢这个答案,尤其是最近我的一位同事遇到了这个问题(需要为某些公共成员的检索添加一些逻辑),因此他将问题变量转换为FUNCTIONS,它适用于代码的所有“get”等效部分,同时破坏了我们实际分配值的少数地方。它最终很容易通过和修复;但事后看来,财产本来是要走的路。我还想补充一点,这个雄辩的答案是猴子写的,给我留下了深刻的印象。【参考方案3】:

本质上的属性获取/设置方法对。

属性是一种运行时支持的方法,它公开一对具有元数据支持的 get set 方法,这意味着可以使用反射发现它们,而无需根据方法名称和签名猜测哪些方法应该构成访问器。

属性的另一个优点是它们在语法上类似于字段,而不是类似于方法,后者的优点是可以创建更简洁的代码。

我倾向于认为 get/set 函数比 a 更具可读性 属性,因为它明确地是 功能而不是直截了当 价值。

大多数时候属性是由 Jit 引擎内联的,因为它们非常简单,这意味着大多数时候属性更像字段而不是函数,因此它们更接近字段的行为而不是函数。

在属性的情况下,函数调用和字段访问之间是否存在歧义并不重要,因为在大多数情况下,您不需要支付函数调用成本,属性 getter 和 setter,因为它们的简单性,是内联的高候选者,这意味着成本方面比函数调用更接近字段*。

注意:并非所有属性都很便宜,但一般准则规定它们应尽可能简单和轻量级。

【讨论】:

【参考方案4】:

除了将属性用于描述对象的属性的值的语义正确性之外,您不能对此提出异议:

obj.SetValue(obj.GetValue() + 1);

obj.Value++;

【讨论】:

【参考方案5】:

它们允许该类型的用户使用简化的语法,并允许您创建只读和只写字段。 因此 我.SetAge(34); 年龄 = 我.GetAge(); 变成 me.age = 34; 年龄 = 我的年龄;

【讨论】:

【参考方案6】:

正如usr 所说:

"一个属性有get的内涵 没有副作用,两者兼而有之 get 和 set 是一个快速的操作。”

没错。这意味着 getter/setter 会很快。通过将某些内容公开为属性,您暗示您正在快速获取/放置对象上的属性。方法用于做某种形式的工作,假设涉及更多的周期,而不是简单地获取/设置属性。我们通常会在 GetFoo(...)/SetFoo(...) 方法中放入一个冗长的操作 'properties' 来表示计算操作比属性重。

【讨论】:

【参考方案7】:

也许是一个小问题,但是对于 getter/setter,我觉得很烦人,当我在带有“intellisense”的 IDE 中循环浏览它们时,有一大块“getter”彼此相邻,而另一块“setter” '。我发现更难找到我正在寻找的东西。

【讨论】:

【参考方案8】:

查看面向对象编程的一种流行方法是根据我们头脑中的概念对程序中的类进行建模。

我们头脑中的概念是基于我们感知到的周围的实际物体(无论是我们感知它们还是我们与感知它们的其他人交流)。

我们周围的物体——家具、动物、航天飞机等——具有特定的属性并以特定的方式发挥作用。

这就是我们获取属性和方法的地方。

在 C# 中,属性可能无法简化为单个字段获取或字段集(例如,它可能需要额外的检查,或者可能涉及缓存或任何数量的原因)。因此,我们需要一个单独的属性概念,包括 get-methods 和 set-methods,以使我们的程序更接近我们希望它们建模的概念。

【讨论】:

关于可能需要额外检查、缓存等:是的,但这并不能区分属性和一对 get/set 方法,是吗?从概念上讲,我和你在一起。但我也认为,如果这是 .NET 属性的唯一目的,那么它们不会为可用的工具添加大量内容。如果我有一个函数 Box.getWidth(),我仍然在为一个对象建模并提供一种访问其特征的方法,就像我使用属性 Box.Width 一样。当然,属性也有其他有用的特性,正如其他答案所表明的那样。 是的,getWidth/setWidth 可以模拟对象属性的概念。但是他们没有像属性的专用语法那样做到这一点。编程的重点是帮助我们将知识转移到计算机上,因此我们设计的语言与我们的思维方式兼容。在我们的思维方式中,现实世界的对象具有属性,而不是 getter 和 setter。我们根据具有特定属性并以特定方式运行的对象来思考这个想法,它是您问题的许多其他答案的基础。【参考方案9】:

属性的两大优势:

它们可以在属性网格中显示和编辑。如果没有属性,您怎么知道要为哪些方法显示编辑器? 它们暗示了某些东西的读写成本很低,而且阅读它不会产生副作用。 (当然,有可能违反这一点,但是通过将某些东西设为属性,您就声明这是您的意图。)因此,调试器可以接受该提示。如果将鼠标悬停在属性上,调试器将在工具提示中显示其值。对任何可能的方法做同样的事情是没有意义的。太容易不小心做了有副作用的事情。

【讨论】:

我喜欢你的第一点。关于您的第二个:我可以看到您来自哪里,但这不是属性的实际特征,而仅仅是鼓励的约定吗?毕竟,.NET 可以提供一个“WellCodedFunction”构造来鼓励开发人员编写好代码,但它实际上不会添加任何真正的功能。 当然,他们可以制作一个“[FastWithNoSideEffects]”属性来指示“在调试器中显示结果安全”,然后只在具有非空返回值的无参数函数上关注它.但是属性似乎是一个更简单的解决方案。【参考方案10】:

不仅在语言中而且在 clr 中具有属性意味着 .NET 上的每个人都可以依赖他们的元数据。属性的含义是 get 没有副作用,并且 get 和 set 都是快速操作。许多工具都使用这些假设:winforms 设计器、LINQ to SQL...

因此,这不仅关乎方便,还关乎拥有额外的元数据。

以下是其他典型假设:

customer.Name = "a";
Assert.IsTrue(customer.Name == "a");

try  var ignored = customer.Name; 
catch  Assert.Fail("Exceptions are not expected"); 

【讨论】:

【参考方案11】:

至少对于 DataBinding 没有它,与 UI 相关的开发会变得更加复杂。

【讨论】:

【参考方案12】:

我相信 XML 序列化只读取/写入公共属性,因此您的 get 和 set 方法将被忽略。

此外,如果您有一个通用的对象列表,您可以将其分配给 DataGridView.DataSource,您将获得每个属性的列。这可能就是@LPalmer 所指的。

【讨论】:

这两件事我都不知道。我还没有真正对序列化做过任何工作(还),但是你关于列生成的第二点实际上对我来说真的很方便——谢谢!【参考方案13】:

我知道在某些情况下,您可以像在数据集中一样将属性用作“列”名称。我认为 .NET 通过自省来做到这一点。我不相信使用 get/set 函数可以做到这一点。

【讨论】:

【参考方案14】:

对我来说,这很容易:

myprop = myvalue;
console.writeline(myprop);

不需要

mysetfunc(myvalue);
console.writeline(mygetprop);

一件事比两件事更容易记住

【讨论】:

mygetprop 似乎有一个错字。它应该是 mygetprop()【参考方案15】:

属性(特别是 .net 3.5 中的自动属性)比 setter/getter 更简洁,代码行更少 == 需要维护的代码更少 == 错误更少。

我会先说可读性,但你已经说过这对你来说不算数.. :)

【讨论】:

我刚刚阅读了关于自动属性的内容:是的,这点很好。但是,对于常规的旧(3.5 之前)属性,它们如何更简洁?我是出于好奇而不是争论:.NET 属性,正如我所熟悉的那样,似乎需要与 get/set 函数相同数量的代码行。但你这么说让我觉得我可能错过了什么。 你说得对的行数,我什至没有注意到.. 我想我使用自动属性的时间太长了.. LBushkin 上面的回答很棒。 理论上您可以将 get 和 set 方法放在文件的不同端。属性不会让你这样做。我之所以提到这一点,是因为我多次遇到代码没有逻辑对齐的问题。

以上是关于为啥我们使用 .NET 属性而不是普通的旧 get/set 函数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥LinkedList中的类节点定义为静态而不是普通类[重复]

为啥parallelStream 使用ForkJoinPool,而不是普通的线程池?

为啥我使用@classmethod 而不是普通的实例方法[重复]

为啥我们更喜欢在角度中使用 $q 而不是 $http [重复]

当我使用 std::algorithms 而不是普通循环时,为啥这段代码会变慢?

为啥 set/get_default_resource 使用指针而不是引用?