在C#中,是什么使字段与属性不同?何时应该使用字段而不是属性?
当前回答
由于他们中的许多人已经解释了财产和Field的技术优缺点,现在是时候进入实时示例了。
1.财产允许您设置只读访问级别
考虑dataTable.Rows.Count和dataTable.Column[i].Caption的情况。它们来自dataTable类,对我们来说都是公共的。对它们的访问级别的不同之处在于,我们不能将值设置为dataTable.Rrows.Count,但我们可以读写dataTable.Colomn[i].Taption。这可以通过Field实现吗?不这只能通过财产完成。
public class DataTable
{
public class Rows
{
private string _count;
// This Count will be accessable to us but have used only "get" ie, readonly
public int Count
{
get
{
return _count;
}
}
}
public class Columns
{
private string _caption;
// Used both "get" and "set" ie, readable and writable
public string Caption
{
get
{
return _caption;
}
set
{
_caption = value;
}
}
}
}
2.PropertyGrid中的财产
您可能在Visual Studio中使用了Button。它的财产显示在PropertyGrid中,如Text、Name等。当我们拖放按钮时,当我们单击财产时,它将自动找到类button并过滤财产,并在Property Grid中显示该类(其中PropertyGrid不会显示Field,即使它们是公共的)。
public class Button
{
private string _text;
private string _name;
private string _someProperty;
public string Text
{
get
{
return _text;
}
set
{
_text = value;
}
}
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
[Browsable(false)]
public string SomeProperty
{
get
{
return _someProperty;
}
set
{
_someProperty= value;
}
}
在PropertyGrid中,将显示财产Name和Text,但不显示SomeProperty。为什么?因为财产可以接受属性。如果[可浏览(false)]为false,则不会显示。
3.可以在财产中执行语句
public class Rows
{
private string _count;
public int Count
{
get
{
return CalculateNoOfRows();
}
}
public int CalculateNoOfRows()
{
// Calculation here and finally set the value to _count
return _count;
}
}
4.绑定源中只能使用财产
绑定源帮助我们减少代码行数。BindingSource不接受字段。为此,我们应该使用财产。
5.调试模式
考虑我们使用Field来保存值。在某些时候,我们需要调试并检查该字段的值在哪里变为空。当代码行数超过1000行时,这将很难做到。在这种情况下,我们可以使用Property,并可以在Property中设置调试模式。
public string Name
{
// Can set debug mode inside get or set
get
{
return _name;
}
set
{
_name = value;
}
}
其他回答
使用财产,您可以在属性值更改时(也称为PropertyChangedEvent)或在值更改为支持取消之前引发事件。
这对于(直接访问)字段是不可能的。
public class Person {
private string _name;
public event EventHandler NameChanging;
public event EventHandler NameChanged;
public string Name{
get
{
return _name;
}
set
{
OnNameChanging();
_name = value;
OnNameChanged();
}
}
private void OnNameChanging(){
NameChanging?.Invoke(this,EventArgs.Empty);
}
private void OnNameChanged(){
NameChanged?.Invoke(this,EventArgs.Empty);
}
}
在后台,属性被编译为方法。因此,Name属性被编译为get_Name()和set_Name(字符串值)。如果您研究编译的代码,您可以看到这一点。因此,在使用它们时会有(非常)小的性能开销。通常,如果向外部公开字段,则始终使用Property,如果需要验证值,则通常在内部使用Property。
IMO,财产只是我们以前使用的“SetXXX()”“GetXX()”函数/方法/接口对,但它们更简洁优雅。
财产支持非对称访问,即您可以有getter和setter,也可以只有这两者之一。类似地,财产支持getter/setter的单独可访问性。字段始终是对称的,即您始终可以获取和设置值。例外情况是只读字段,在初始化后显然无法设置。
财产可能运行很长时间,有副作用,甚至可能引发异常。字段很快,没有副作用,并且不会抛出异常。由于副作用,属性可能会为每个调用返回不同的值(DateTime.Now可能就是这样,即DateTime.Nnow不总是等于DateTime.NNow)。字段总是返回相同的值。
字段可以用于out/ref参数,财产则不能。财产支持额外的逻辑–这可以用于实现延迟加载等。
财产通过封装获取/设置值的任何含义来支持抽象级别。
在大多数/所有情况下使用财产,但尽量避免副作用。
一个重要的区别是接口可以有财产,但不能有字段。对我来说,这强调了财产应该用于定义类的公共接口,而字段则用于类的私有内部工作。通常,我很少创建公共字段,类似地,我也很少创建非公共财产。