我们经常被告知我们应该通过在类字段中使用getter和setter方法(C#中的属性)来保护封装,而不是将字段暴露给外部世界。
但是很多时候,一个字段只是用来保存一个值,并且不需要任何计算来获取或设置。对于这些,我们都会这样做:
public class Book
{
private string _title;
public string Title
{
get{ return _title; }
set{ _title = value; }
}
}
好吧,我有一个坦白,我不忍心写下所有这些(真的,它不必写它,它不得不看它),所以我去流氓并使用公共领域。
然后是C#3.0,我看到他们添加了自动属性:
public class Book
{
public string Title {get; set;}
}
更整洁,我很感激,但是真的,除了创建公共领域之外有什么不同呢?
public class Book
{
public string Title;
}
答案 0 :(得分:160)
在我前段时间的related question中,有一条链接指向杰夫博客上的帖子,解释了一些差异。
Properties vs. Public Variables
将变量更改为属性是一个重大变化。例如:
TryGetTitle(out book.Title); // requires a variable
答案 1 :(得分:76)
忽略API问题,我认为使用属性最有价值的是调试。
CLR调试器不支持数据断点(大多数本机调试器都支持)。因此,无法在类的特定字段的读取或写入上设置断点。在某些调试方案中,这是非常有限的。
由于属性是以非常精简的方法实现的,因此可以在读取和写入其值时设置断点。这使他们在田野上占了一席之地。
答案 2 :(得分:61)
从字段更改为属性会破坏合同(例如,需要重新编译所有引用代码)。因此,当您与其他类(任何公共(通常受保护的)成员)建立交互点时,您希望计划未来的增长。通过始终使用属性来实现。
今天没有什么可以让它成为一个自动财产,并且3个月后就意识到你想让它延迟加载,并在getter中进行空检查。如果你使用了一个字段,这是一个最好的重新编译更改,最坏的情况是不可能的,这取决于谁和&还有什么依赖于你的装配。
答案 3 :(得分:57)
仅仅因为没人提到它:你不能在接口上定义字段。因此,如果必须实现定义属性的特定接口,则自动属性有时是一个非常好的功能。
答案 4 :(得分:44)
经常被忽视的巨大差异在任何其他答案中都没有提及:覆盖。您可以声明属性虚拟并覆盖它们,而不能对公共成员字段执行相同的操作。
答案 5 :(得分:10)
自动实现的属性相对于公共字段的另一个优点是,您可以将set访问器设置为私有或受保护,从而提供对象类,其中定义的对象类比公共字段更好地控制。
答案 6 :(得分:8)
制作字段public
没有错。但请记住,使用getter/setter
字段创建private
并不是封装。 IMO,如果您不关心Property
的其他功能,您也可以public
。
答案 7 :(得分:7)
关于版本控制和API稳定性。在版本1中没有区别 - 但是稍后,如果您决定需要在版本2中将此属性设置为某种类型的错误检查,则无需在任何地方更改API,无需更改代码,财产的定义。
答案 8 :(得分:0)
如果您稍后决定检查标题是否唯一,则通过与集合或数据库进行比较,您可以在属性中执行此操作,而无需更改任何依赖于它的代码。
如果你只使用公共属性,那么灵活性就会降低。
在不违反合同的情况下,额外的灵活性对我来说最重要的是使用属性,并且在我真正需要灵活性之前,自动生成是最有意义的。
答案 9 :(得分:0)
我觉得非常有用的一件事以及所有代码和测试原因是,如果它是属性vs字段,则Visual Studio IDE会显示属性的引用但不显示字段。
答案 10 :(得分:0)
做了一些研究后我的观点
它确实为我们提供了更多的可能性和可扩展性。
答案 11 :(得分:0)
您可以对Fields进行操作,但不能对Properties进行操作(或者以前无法使用...一会儿就可以解决),您可以将Fields指定为readonly
,而属性不能。因此,Fields为您提供了一种明确的方式来表明您的意图,即仅在对象实例化时(从构造函数内部)设置变量,并且此后不应更改。是的,您可以将Property设置为具有私有setter,但是只说“这不能从类外部更改”,这与“在实例化后不要更改”不同–您仍然可以在课堂上实例化后进行更改。是的,您可以将属性的后备字段设置为只读,但这会使实例化后的尝试将其更改为运行时错误,而不是编译时错误。因此,只读字段做了一些有用的事情,而属性却无法做到。
但是,随着C#9的改变,我们获得了针对属性的有用语法:
public string Height { get; init; }
表示“可以从类外部使用它,但只能在初始化对象时设置它”,因此,Fields的只读优点消失了。