何时使用属性和方法?

我是来自C ++的.NET世界的新手,我正在努力更好地理解属性。 我注意到在.NET框架中,Microsoft使用了所有地方的属性。 使用属性而不是创建get / set方法是否有优势? 当一个人应该使用属性时,是否有一般指南(以及命名约定)?

它是纯粹的语法糖。 在后端,它被编译为普通的get和set方法。

因为惯例使用它,它看起来更好。

一些指导原则是,当它具有抛出exception或出错的高风险时,不要使用属性,而应使用明确的getter / setter。 但一般来说,即使这样他们也会被使

属性 get / set方法; 简单地说,它将它们forms化为单个概念(用于读取和写入),允许(例如)针对属性的元数据,而不是单个成员。 例如:

[XmlAttribute("foo")] public string Name {get;set;} 

这是一对get / set方法,但附加元数据适用于两者。 IMO也简单易用:

 someObj.Name = "Fred"; // clearly a "set" DateTime dob = someObj.DateOfBirth; // clearly a "get" 

我们没有重复我们正在进行获取/设置的事实。

另一个好处是它允许对属性(上面的“Name”)进行简单的双向数据绑定,而不依赖于任何魔术模式(编译器保证的那些除外)。

有一本完整的书专门回答这些问题:Addison-Wesley的框架设计指南 。 有关何时选择属性与方法的建议,请参阅第5.1.3节。

本书的大部分内容也可以在MSDN上找到,但我觉得把它放在桌面上很方便。

考虑阅读属性和方法之间的选择 。 它有很多关于.NET设计指南的信息。

属性 get / set方法

设置属性并获取方法,就像这里的人已经解释过的那样,但是使用它们的想法是使这些方法成为唯一使用私有值的方法(例如,处理validation)。

应该针对属性完成整个其他逻辑,但是在操作(属性)的左侧和右侧处理您可以处理的事物并且甚至不必认为它是方法时,在心理上总是更容易。

我个人认为这是属性背后的主要思想。

我一直认为属性是类的名词,其中方法是动词……

首先,命名约定是:使用PascalCase作为属性名称,就像使用方法一样。 此外,属性不应包含非常复杂的操作。 这些应该用方法保存。

在OOP中,您可以将对象描述为具有属性和function。 你在设计课时这样做。 考虑设计一辆汽车。 function的示例可以是移动到某处或激活刮水器的能力。 在你的课堂上,这些都是方法。 属性是指在给定时刻汽车内的乘客数量。 如果没有属性,您将有两种方法来实现该属性:

公开变量:

 // class Car public int passengerCount = 4; // calling code int count = myCar.passengerCount; 

这有几个问题。 首先,它并不是车辆的真正属性。 您必须更新Car类中的值以使其表示车辆的真实状态。 其次,变量是公共的,也可以写入。

第二个变体是使用的一个widley,例如在Java中,你没有c#中的属性:

使用方法封装值,并可能先执行一些操作。

 // class Car public int GetPassengerCount() { // perform some operation int result = CountAllPassengers(); // return the result return result; } // calling code int count = myCar.GetPassengerCount(); 

通过这种方式,您可以设法解决公共变量的问题。 通过询问乘客人数,您可以确保在回答之前获得最新的结果。 此外,您无法更改该值,因为该方法不允许它。 但问题是,你确实希望乘客数量成为一个属性,而不是你汽车的function。

第二种方法不一定是错的,它只是读得不对。 这就是为什么有些语言包含使属性看起来像变量的方法,即使它们像幕后的方法一样工作。 例如,Actionscript还包括定义将在调用代码中以变量样式访问的方法的语法。

请记住,这也带来了责任。 调用用户将期望它的行为类似于属性,而不是函数。 因此,如果只是询问一辆车有多少乘客需要20秒加载,那么你可能应该用真正的方法打包,因为调用者希望函数花费的时间比访问属性要长。

编辑:我几乎忘了提到这个:在设置变量之前实际执行某些检查的能力。 通过使用公共变量,您基本上可以在其中写入任何内容。 setter方法或属性使您有机会在实际保存之前对其进行检查。

属性只是节省了一些时间来编写与get / set方法一起使用的样板文件。

话虽这么说,很多.NET的东西处理不同的属性 – 例如,网格将自动显示属性,但不会显示相同的function。

这很方便,因为您可以为不想显示的内容创建get / set方法,并为需要显示的内容创建属性。

编译器实际为您定义的每个属性发出get_MyProperty和set_MyProperty方法。

虽然它不是一个硬性规则,并且正如其他人所指出的那样,属性在幕后实现为Get / Set对 – 通常是Properties表面封装/保护状态数据,而方法(也称为过程或函数)确实有效,产生这项工作的结果。

因此,方法将经常采用他们可能仅仅消耗的参数,但也可能以改变的状态返回,或者可能由于完成的工作而产生新的对象或值。

一般而言 – 如果您需要一种控制对数据或状态的访问的方法,那么Properties允许实现以定义的,可validation的和优化的方式访问(允许访问限制,范围和错误检查,按需创建后备存储和避免冗余设置调用的方法)。

相比之下,方法转换状态并在内部和外部产生新值,而不一定是可重复的结果。

当然,如果你发现自己在一个属性中编写程序或变形代码,你可能真的在编写一个方法。

另请注意,属性可通过reflection获得。 虽然方法也是,但属性代表对象的“有趣的东西”。 如果您正在尝试显示对象的属性网格 – 比如像Visual Studio表单设计器那样 – 那么您可以使用reflection来查询类的属性,遍历每个属性,并查询对象的内容值。

可以这样想,Properties封装你的字段(通常标记为私有),同时为你的开发人员提供设置或获取字段值。 如果需要,您甚至可以在属性的set方法中执行例行validation。

属性不仅仅是语法糖 – 如果你需要创建对象关系映射( Linq2Sql或Linq2Entities ),它们很重要,因为它们的行为就像变量一样,同时可以隐藏对象关系映射(持久性)的实现细节。 还可以在属性的getter中validation分配给它的值,并保护它不会分配不需要的值。

你不能用同样优雅的方法来做到这一点。 我认为最好用一个实际例子来certificate这一点。

在他的一篇文章中,Scott Gu使用“代码优先”方法创建映射到Northwind数据库的类。 一个简短的例子来自Scott的博客(稍加修改,完整的文章可以在Scott Gu的博客上阅读):

 public class Product { [Key] public int ProductID { get; set; } public string ProductName { get; set; } public Decimal? UnitPrice { get; set; } public bool Discontinued { get; set; } public virtual Category category { get; set; } } // class Category omitted in this example public class Northwind : DbContext { public DbSet Products { get; set; } public DbSet Categories { get; set; } } 

您可以使用实体集ProductsCategories和相关类ProductCategory就像它们是包含变量的普通对象一样:您可以读取和写入它们,它们的行为就像普通变量一样。 但是你也可以在Linq查询中使用它们,坚持它们(将它们存储在数据库中并检索它们)。 另请注意 ,使用注释 (C#属性)定义主键是多么容易(在此示例中, ProductIDProduct的主键)。

虽然这些属性用于定义存储在数据库中的数据的表示,但实体集类中定义了一些控制持久性的方法:例如,方法Remove()将给定实体标记为已删除,而Add()添加一个给定的实体, SaveChanges()使更改成为永久性的。 您可以将这些方法视为操作(即您可以控制要对数据执行的操作)。

最后,我举一个例子,你可以自然地使用这些类:

 // instantiate the database as object var nw = new NorthWind(); // select product var product = nw.Products.Single(p => p.ProductName == "Chai"); // 1. modify the price product.UnitPrice = 2.33M; // 2. store a new category var c = new Category(); c.Category = "Example category"; c.Description = "Show how to persist data"; nw.Categories.Add(c); // Save changes (1. and 2.) to the Northwind database nw.SaveChanges();