我们经常被告知,应该通过为类字段创建getter和setter方法(c#中的属性)来保护封装,而不是将字段暴露给外界。
但是很多时候,一个字段只是用来保存一个值,不需要任何计算来获取或设置。对于这些问题,我们都会做这个数字:
public class Book
{
private string _title;
public string Title
{
get => _title;
set => _title = value;
}
}
好吧,我有一个忏悔,我不能忍受写所有这些(真的,不是必须写它,而是必须看它),所以我擅自使用了公共字段。
然后出现了c# 3.0,我看到他们添加了自动属性:
public class Book
{
public string Title { get; set; }
}
哪个更整洁,我很感激,但说真的,这和仅仅创建一个公共字段有什么不同呢?
public class Book
{
public string Title;
}
One thing you can do with Fields but not with Properties (or didn't used to be able to ... I'll come to that in a moment) is that Fields can be designated as readonly whereas Properties cannot. So Fields give you a clear way of indicating your intention that a variable is there to be set (from within the constructor) at object-instantiation time only and should not be changed thereafter. Yes, you can set a Property to have a private setter, but that just says "this is not to be changed from outside the class", which is not the same as "this is not to be changed after instantiation" - you can still change it post-instantiation from within the class. And yes you can set the backing field of your property to be readonly, but that moves post-instantiation attempts to change it to being run-time errors rather than compile-time errors. So readonly Fields did something useful which Properties cannot.
然而,这在c# 9中发生了变化,我们得到了下面这些有用的属性语法:
public string Height { get; init; }
它说“这个可以从类外使用,但它只能在对象初始化时设置”,因此Fields的只读优势消失了。
Trivial properties like these make me sad. They are the worst kind of cargo culting and the hatred for public fields in C# needs to stop. The biggest argument against public fields is future-proofing: If you later decide you need to add extra logic to the getter and setter, then you will have to do a huge refactor in any other code that uses the field. This is certainly true in other languages like C++ and Java where the semantics for calling a getter and setter method are very different from those for setting and getting a field. However, in C#, the semantics for accessing a property are exactly the same as those for accessing a field, so 99% of your code should be completely unaffected by this.
我所见过的一个将字段更改为属性的例子实际上是源代码级别的破坏性更改,如下所示:
TryGetTitle(out book.Title); // requires a variable
对此,我要问,为什么TF要传递其他类的字段作为引用?依赖于它不是一个属性似乎是这里真正的编码失败。假设您可以直接写入另一个类中的数据,而您对此一无所知,这是一种糟糕的实践。制作你自己的局部变量和集书。标题就是这样的。任何做这种事情的代码都应该被打破。
我还看到了其他反对的观点:
Changing a field to a property breaks binary compatibility and requires any code that uses it to be recompiled: This is a concern iff you are writing code for distribution as a closed-source library. In that case, yes, make sure none of your user-facing classes have public fields and use trivial properties as needed. If however you are like 99% of C# developers and writing code purely for internal consumption within your project, then why is recompilation a big concern? Just about any other change you make is going to require recompilation too, and so what if it does? Last I checked, it is no longer 1995, we have fast computers with fast compilers and incremental linkers, even larger recompilations shouldn't need more than a few minutes, and it has been quite some time since I have been able to use "my code's compiling" as an excuse for swordfighting through the office.
You can't databind against a variable: Great, when you need to do that, make it into a property.
Properties have features that make them better for debugging like reflection and setting breakpoints: Great, one you need to use one of those things, make it into a property. When you're done debugging and ready to release, if you don't still need those functionalities, change it back into a field.
Properties allow you to override behavior in derived classes: Great, if you are making a base class where you think such a scenario is likely, then make the appropriate members into properties. If you're not sure, leave it as a field and you can change it later. Yes, that will probably require some recompilation, but again, so what?
所以总的来说,是的,对于一些不重要的属性有一些合法的用途,但是除非你是在为公共发布制作一个闭源库,否则在需要的时候,字段很容易转换成属性,而对公共字段的非理性恐惧只是一些面向对象的教条,我们应该很好地摆脱它。