CSharp - c# Field和Property之间的区别是什么?

  显示原文与译文双语对照的内容

在 C# 中,字段与属性有什么不同,什么时候应使用字段而不是属性?

时间:

属性公开字段。字段应保留为私有类,通过获取和设置属性访问。 属性提供了一个抽象级别,允许你在不影响使用类访问的外部方式的情况下更改字段。


public class MyClass
{
//this is a field. It is private to your class and stores the actual data.
 private string _myField;

//this is a property. When you access it uses the underlying field, but only exposes
//the contract that will not be affected by the underlying field
 public string MyField
 {
 get
 {
 return _myField;
 }
 set
 {
 _myField = value;
 }
 }
}

@Kent 指出属性不是封装字段所必需的,它们可以在其他字段上进行计算,或者提供其他目的。

@GSS 指出你也可以在访问属性时进行其他逻辑,比如验证,另一个有用的特性。

面向对象的编程原则说,类的内部工作应该隐藏在外部。 如果你暴露一个字段,你本质上暴露了类的内部实现。 因此,我们用java的属性( 或者方法以防) 来包装字段,让我们能够改变实现而不破坏代码。 鉴于我们可以在属性中放入逻辑,也允许我们在需要时执行验证逻辑等。 C# 3有可能混淆的autoproperties概念。 这允许我们简单地定义属性,C#3编译器将为我们生成私有字段。


public class Person
{
 private string _name;

 public string Name
 {
 get
 {
 return _name;
 }
 set
 {
 _name = value;
 }
 }
 public int Age{get;set;}//AutoProperty generates private field for us
}

我将给你几个使用属性的例子,这些属性可能会使齿轮转动:

  • 惰性初始化: 如果你的对象的属性非常昂贵,但在代码正常运行时无法访问,则可以通过属性延迟加载。 这样,它只是坐在那里,但是第一次模块试图调用这个属性时,它会检查底层字段是否为空,如果它是空的,那么它就会被调用。 这可以大大加速对象初始化。
  • 在 stackoverflow, 脏跟踪: 这里,事实上我还学习了关于从我自己的问题 当我有很多对象可能已经更改的值在运行,我可以使用该属性来跟踪过程中如果需要进行保存回数据库与否。 如果不是一个对象的单一属性更改,则IsDirty标志不会被触发,因此在决定返回数据库时,保存功能将跳过它。

一个重要的区别是接口可以有属性,但不能有字段。 对于我来说,这强调了属性应该用于定义类接口的公共属性,而字段则用于类的私有内部工作。 作为一个规则我很少创建公共字段,同样我很少创建非公共属性。

使用属性,当属性的值更改时,可以引发事件( 例如。 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(){
 EventHandler localEvent = NameChanging;
 if (localEvent!= null) {
 localEvent(this,EventArgs.Empty);
 }
 }

 private void OnNameChanged(){
 EventHandler localEvent = NameChanged;
 if (localEvent!= null) {
 localEvent(this,EventArgs.Empty);
 }
 }
}

差异- 使用( 什么时候和为什么)

字段是直接在类或者结构中声明的变量。 类或者结构可能有实例字段或者静态字段或者两者都有。 通常,你应该只对具有私有或者受保护的辅助功能的变量使用字段 。 你的类向客户端代码公开的数据应该通过方法属性和索引器提供。 通过使用这些构造间接访问内部字段,可以针对无效的输入值提供防护。

属性是一个成员,它提供一个灵活的机制来读取,写入或者计算私有字段的值。 属性可以作为公共数据成员使用,但它们实际上是称为访问器的特殊方法。 这样就可以轻松访问数据,此外还有助于提高 安全和灵活性的方法。 属性使类能够以一种公开的方法获取和设置值,同时隐藏实现或验证代码. 获取属性访问器用于返回属性值,集合访问器用于分配新值。

属性的主要优点是允许你更改访问对象上数据的方式而不破坏它的公共接口。 例如如果你需要添加额外的验证,或者将存储的字段更改为计算的字段,那么如果你最初将字段作为属性公开,那么就可以很容易地进行。 如果直接公开一个字段,则必须更改类的公共接口以添加新的功能。 这种更改会破坏现有的客户端,要求它们在使用新版本的代码之前重新编译。

如果你编写了一个用于广泛消费( 与. NET 框架一样,数百万人使用它)的类库,这可能是一个问题。 但是,如果你正在编写一个内部使用的小代码库( 说 <= 50 K 行)的类,这实际上没什么大不了的,因为没有人会受到你的更改的负面影响。 在这种情况下,它真正的归结到个人偏好。

属性支持非对称访问,换句话说,可以有getter和 setter,也可以只包含两个。 类似的属性支持 getter/setter的单个可以访问性。 字段总是对称的,换句话说,总是可以获取和设置值。 例外的是只读字段,在初始化后显然无法设置。

属性可能运行很长时间,有副作用,甚至可能引发异常。 字段是快速的,没有副作用,不会抛出异常。 由于副作用,属性可能为每个调用返回一个不同的值( DateTime.Now, 换句话说,DateTime.Now的情况并非总是等于 datetime.now ) 。 字段总是返回相同的值。

字段可以用于输出/引用参数,属性可能不能。 属性支持额外的逻辑- 这可以用来实现其他事物的延迟加载。

属性通过封装获取/设置值的方法来支持抽象级别。

在大多数/所有情况下使用属性,但尽量避免副作用。

在背景中,属性被编译到方法中。 因此 Name 属性被编译到 get_Name()set_Name(string value) 中。 如果你研究编译后的代码,你可以看到。 所以在使用( 非常) 时,有一个很小的性能开销。 通常情况下,如果向外部公开字段,则总是使用属性,如果需要验证值,通常会在内部使用它。

当你想让你的私有 variable(field) 从其他类访问你的私有时,你需要为这些变量创建属性。

例如如果我有名为"标识"和"姓名"的变量,它是私有的,但在类之外的读取/写入操作中可能需要这个变量。 在这种情况下,属性可以帮助我根据为属性定义的获取/集合来读取/写入变量。 属性可以是 readonly/writeonly/readwrite,也可以是。

演示如下


class Employee
{
//Private Fields for Employee
 private int id;
 private string name;

//Property for id variable/field
 public int EmployeeId
 {
 get
 {
 return id;
 }
 set
 {
 id = value;
 }
 }

//Property for name variable/field
 public string EmployeeName
 {
 get
 {
 return name;
 }
 set
 {
 name = value;
 }
 }
}

class MyMain
{
 public static void Main(string [] args)
 {
 Employee aEmployee = new Employee();
 aEmployee.EmployeeId = 101;
 aEmployee.EmployeeName ="Sundaran S";
 }
}

...