字段和属性有什么区别?

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

630380 次浏览

属性公开字段。字段应该(几乎总是)对类保持私有,并通过get和set属性访问。属性提供了一个抽象级别,允许您更改字段,同时不影响使用您的类的事物访问它们的外部方式。

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 accessed it uses the underlying field,// but only exposes the contract, which will not be affected by the underlying fieldpublic string MyProperty{get{return _myField;}set{_myField = value;}}
// This is an AutoProperty (C# 3.0 and higher) - which is a shorthand syntax// used to generate a private field for youpublic int AnotherProperty { get; set; }}

@Kent指出,Properties不需要封装字段,它们可以对其他字段进行计算,或用于其他目的。

@GSS指出,您还可以在访问属性时执行其他逻辑,例如验证,这是另一个有用的功能。

面向对象编程原则说,类的内部工作应该向外界隐藏。如果你暴露了一个字段,本质上你暴露了类的内部实现。因此我们用Properties(或Java的方法)包装字段,以便我们能够在不破坏代码的情况下更改实现。因为我们可以将逻辑放在Property中,也允许我们在需要时执行验证逻辑等。C#3有可能令人困惑的自动属性概念。这允许我们简单地定义属性,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}

如果您要使用线程原语,您将被迫使用字段。属性可能会破坏您的线程代码。除此之外,cory所说的是正确的。

属性封装字段,从而使您能够对要设置或检索的值执行附加处理。如果您不对字段值进行任何预处理或后处理,则使用属性通常是多余的。

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

如果你编写的类库是为广泛使用而设计的(比如. NET Framework,有数百万人在使用),这可能是个问题。然而,如果你是在一个小代码库中编写一个内部使用的类(比如<=50 K行),这真的没什么大不了的,因为没有人会因为你的更改而受到不利影响。在这种情况下,这实际上只是个人偏好。

此外,属性允许您在设置值时使用逻辑。

因此,您可以说您只想将值设置为整数字段,如果该值大于x,则抛出异常。

真正有用的功能。

在后台,属性被编译为方法。因此,Name属性被编译为get_Name()set_Name(string value)。如果您研究编译代码,您可以看到这一点。因此,使用它们时存在(非常)小的性能开销。通常,如果您将字段公开给外部,您将始终使用属性,如果需要对值进行验证,您将经常在内部使用它。

属性支持非对称访问,即您可以有一个getter和一个setter,也可以只有两个中的一个。类似地,属性支持getter/setter的单独可访问性。字段始终是对称的,即您始终可以获取和设置值。例外的是只读字段,它们显然在初始化后无法设置。

属性可能会运行很长时间,有副作用,甚至可能抛出异常。字段速度快,没有副作用,永远不会抛出异常。由于副作用,属性可能会为每次调用返回不同的值(DateTime. Now可能就是这种情况,即DateTime. Now并不总是等于DateTime. Now)。字段始终返回相同的值。

字段可以用于out/ref参数,属性不能。属性支持额外的逻辑-这可用于实现延迟加载等。

属性通过封装获取/设置值的任何含义来支持一定程度的抽象。

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

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

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

  • 延迟初始化如果你有一个对象的属性加载起来很昂贵,但是在代码的正常运行中没有被访问那么多,你可以通过该属性延迟它的加载。这样,它就呆在那里,但是当另一个模块第一次尝试调用该属性时,它会检查底层字段是否为空-如果是,它会继续加载它,调用模块不知道。这可以大大加快对象初始化的速度。
  • 脏跟踪:这是我从StackOverflow上的自己的问题中学到的。当我有很多对象的值在运行过程中可能发生了变化时,我可以使用该属性来跟踪它们是否需要保存回数据库。如果对象的单个属性没有更改,IsDirty标志不会被触发,因此在决定需要返回数据库的内容时,保存功能会跳过它。

(这真的应该是一个评论,但我不能发表评论,所以如果它不适合作为帖子,请原谅)。

我曾经在一个地方工作,推荐的做法是使用公共字段而不是属性,而等效的属性def只会访问一个字段,如:

get { return _afield; }set { _afield = value; }

他们的理由是,如果需要,公共领域可以在以后转换为财产。当时我觉得有点奇怪。从这些帖子来看,这里似乎也没有多少人会同意。你可能会说什么来试图改变事情?

编辑:我应该补充一点,这个地方的所有代码库都是同时编译的,所以他们可能认为更改类的公共接口(通过将公共字段更改为属性)不是问题。

使用Properties,您可以在属性的值更改时(又名。属性更改事件)或在更改值以支持取消之前引发事件。

这是不可能的(直接访问)字段。

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);}}

差异-用途(何时以及为何)

领域是直接在类或结构中声明的变量。类或结构可能有实例字段或静态字段或两者兼而有之。通常,您应该使用字段仅适用于具有私有或受保护可访问性的变量。您的类公开给客户端代码应该通过方法、属性来提供和索引器的数据。通过使用这些构造间接访问内部字段,您可以防止无效的输入值。

A财产是一个成员,它提供了一种灵活的机制来读取、写入或计算私有字段的值。属性可以像公共数据成员一样使用,但它们实际上是称为访问器的特殊方法。这使得数据可以轻松访问,并且仍然有助于促进方法的安全性和灵活性。属性使类能够公开获取和设置值的公共方式,同时隐藏实现或校验码。获取属性访问器用于返回属性值,设置访问器用于分配新值。

从技术上讲,我认为没有区别,因为属性只是用户创建的或编译器自动创建的字段的包装器。属性的目的是强制封装并提供轻量级的类似方法的功能。将字段声明为public是一种糟糕的做法,但它没有任何问题。

在我看来,属性只是我们之前使用的“SetXXX()”“GetXXX()”函数/方法/接口对,但它们更简洁优雅。

当您希望您的私有变量(字段)可以从其他类访问您的类的对象时,您需要为这些变量创建属性。

例如,如果我有名为“id”和“name”的变量,这是私有的但是可能会有这样的情况,这个变量需要在类之外进行读/写操作。在这种情况下,属性可以帮助我根据为属性定义的get/set来获取该变量的读/写操作。属性可以是只读/只写/读写两者。

这是演示

class Employee{// Private Fields for Employeeprivate int id;private string name;
//Property for id variable/fieldpublic int EmployeeId{get{return id;}set{id = value;}}
//Property for name variable/fieldpublic 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";}}

由于他们中的许多人已经解释了PropertiesField的技术优缺点,现在是时候进入实时示例了。

1.属性允许您设置只读访问级别

dataTable.Rows.CountdataTable.Columns[i].Caption为例。它们来自类DataTable,对我们都是公共的。它们的访问级别不同在于我们不能将value设置为dataTable.Rows.Count,但我们可以读写dataTable.Columns[i].Caption。这可能通过Field实现吗?不!!!这只能用Properties完成。

public class DataTable{public class Rows{private string _count;
// This Count will be accessable to us but have used only "get" ie, readonlypublic int Count{get{return _count;}}}
public class Columns{private string _caption;
// Used both "get" and "set" ie, readable and writablepublic string Caption{get{return _caption;}set{_caption = value;}}}}

2. Properties tyGrid中的属性

您可能在Visual Studio中使用过Button。它的属性显示在PropertyGrid中,如TextName等。当我们拖放按钮时,当我们单击属性时,它会自动找到类Button和过滤器Properties并显示在PropertyGrid中(其中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中,将显示属性NameText,但不显示SomeProperty。为什么???因为属性可以接受属性。如果[Browsable(false)]为假,它不会显示。

3.可以在Properties内部执行语句

public class Rows{private string _count;

public int Count{get{return CalculateNoOfRows();}}
public int CalculateNoOfRows(){// Calculation here and finally set the value to _countreturn _count;}}

4.只有属性可以在绑定源中使用

结合源帮助我们减少代码行数。BindingSource不接受Fields。我们应该使用Properties

调试模式

假设我们使用Field来保存一个值。在某些时候,我们需要调试并检查该字段的值在哪里变得null。当代码行数超过1000时,这将是困难的。在这种情况下,我们可以使用Property并可以在Property中设置调试模式。

   public string Name{// Can set debug mode inside get or setget{return _name;}set{_name = value;}}

传统上私有字段是通过getter和setter方法设置的。为了减少代码,您可以使用属性来设置字段。

当你有一个类是“Car”时。属性是颜色、形状…

其中as字段是在类的范围内定义的变量。

字段是类的普通成员变量或成员实例。属性是抽象以获取和设置它们的值。属性也称为访问器,因为如果您将类中的字段公开为私有,它们提供了一种更改和检索字段的方法。通常,您应该将您的成员变量声明为私有,然后为它们声明或定义属性。

  class SomeClass{int numbera; //Field
//Propertypublic static int numbera { get; set;}
}

这里的第二个问题,“什么时候应该使用字段而不是属性?”,只是在另一个答案这个也是中简要提及,但没有太多细节。

一般来说,所有其他答案都与好的设计有关:更喜欢公开属性而不是公开字段。虽然你可能不会发现自己说“哇,想象一下如果我把它变成一个字段而不是属性,事情会变得多么糟糕”,但更少的情况是想到“哇,感谢上帝,我在这里使用了一个字段而不是属性。”

但是字段比属性有一个优势,那就是它们可以用作“ref”/“out”参数。假设您有一个具有以下签名的方法:

public void TransformPoint(ref double x, ref double y);

假设您想使用该方法转换像这样创建的数组:

System.Windows.Point[] points = new Point[1000000];Initialize(points);

这是我认为最快的方法,因为XY是属性:

for (int i = 0; i < points.Length; i++){double x = points[i].X;double y = points[i].Y;TransformPoint(ref x, ref y);points[i].X = x;points[i].Y = y;}

这将是非常好的!除非你有测量证明不是这样,否则没有理由发出恶臭。但我相信从技术上讲,它并不能保证像这样快:

internal struct MyPoint{internal double X;internal double Y;}
// ...
MyPoint[] points = new MyPoint[1000000];Initialize(points);
// ...
for (int i = 0; i < points.Length; i++){TransformPoint(ref points[i].X, ref points[i].Y);}

我自己做了一些测量,带字段的版本与带属性的版本(. NET 4.6、Windows 7、x64、发布模式、没有附加调试器)大约需要61%的时间。TransformPoint方法的成本越高,差异就越不明显。要自己重复这个,运行第一行注释掉,而不是注释掉。

即使上面没有性能优势,也有其他地方能够使用ref和out参数可能是有益的,例如调用互锁挥发性系列方法时。注意:如果这对你来说是新的,Volatile基本上是一种获得#0关键字提供的相同行为的方法。因此,像#0一样,它并不能像它的名字暗示的那样神奇地解决所有线程安全问题。

我绝对不想看起来像是在提倡你“哦,我应该开始公开字段而不是属性”,关键是如果你需要在调用“ref”或“out”参数时经常使用这些成员,特别是在可能是简单值类型的东西上,不太可能需要属性的任何增值元素,可以提出一个参数。

来自Wikipedia--面向对象程序设计

面向对象编程(OOP)是一种基于“对象”概念的编程范式,“对象”是包含数据,以字段的形式的数据结构,通常称为属性;和代码,以过程的形式出现,通常称为方法(着重部分由作者标明)

属性实际上是对象行为的一部分,但旨在为对象的消费者提供使用对象数据的幻觉/抽象。

属性是一类特殊的类成员,在属性中我们使用预定义的Set或Get方法。它们使用访问器,我们可以通过访问器读取、写入或更改私有字段的值。

例如,让我们以一个名为Employee的类为例,其中包含用于名称、年龄和Employee_Id的私有字段。我们不能从类外部访问这些字段,但我们可以通过属性访问这些私有字段。

我们为什么要使用属性?

将类字段公开并公开是有风险的,因为您无法控制分配和返回的内容。

为了清楚地理解这一点,我们举一个有ID、密码、姓名的学生类

  • ID不应该是-ve。
  • 名称不能设置为空
  • 通过标记应为只读。
  • 如果缺少学生姓名,则不应返回姓名。

为了解决这个问题,我们使用Get和set方法。

// A simple examplepublic class student{public int ID;public int passmark;public string name;}
public class Program{public static void Main(string[] args){student s1 = new student();s1.ID = -101; // here ID can't be -ves1.Name = null ; // here Name can't be null}}

现在我们以get和set方法为例

public class student{private int _ID;private int _passmark;private string_name ;// for id propertypublic void SetID(int ID){if(ID<=0){throw new exception("student ID should be greater then 0");}this._ID = ID;}public int getID(){return_ID;}}public class programme{public static void main(){student s1 = new student ();s1.SetID(101);}// Like this we also can use for Name propertypublic void SetName(string Name){if(string.IsNullOrEmpty(Name)){throw new exeception("name can not be null");}this._Name = Name;}public string GetName(){if( string.IsNullOrEmpty(This.Name)){return "No Name";}else{return this._name;}}// Like this we also can use for Passmark propertypublic int Getpassmark(){return this._passmark;}}

我对字段的设计是一个字段只需要由它的父级修改,因此类。结果变量变成私有的,然后为了能够授予读取外部类/方法的权利,我只使用Get通过属性系统。然后字段被属性检索并只读!如果你想修改它,你必须通过方法(例如构造函数),我发现多亏了这种让你安全的方式,我们对代码有更好的控制,因为我们“法兰”。一个人可以把所有的事情都公开,所以每一种可能的情况,变量/方法/类等的概念……在我看来只是对代码开发和维护的帮助。例如,如果一个人用公共字段恢复一段代码,他可以做任何事情,因此与目标有关的事情“不合逻辑”,即编写代码的逻辑。这是我的观点。

当我使用经典模型私有字段/公共只读属性时,对于10个私有字段,我应该编写10个公共属性!代码可以更快地变大。我发现了私有设置器,现在我只使用公共属性和私有设置器。setter在后台创建一个私有字段。

这就是为什么我的经典编程风格是:

public class MyClass{private int _id;public int ID { get { return _id; } }public MyClass(int id){_id = id;}}

我的新编程风格:

public class MyClass{public int ID { get; private set; }public MyClass(int id){ID = id;}}

其他信息:默认情况下,get和set访问器与属性本身一样可访问。您可以通过对它们应用更严格的访问修饰符来单独控制/限制访问器可访问性(对于get和set)。

示例:

public string Name{get{return name;}protected set{name = value;}}

这里get仍然是公开访问的(因为属性是公共的),但set是受保护的(更受限制的访问说明符)。

属性用于公开字段。它们使用访问器(set、get),通过这些访问器可以读取、写入或操作私有字段的值。

属性不命名存储位置。相反,它们具有读取、写入或计算其值的访问器。

使用属性,我们可以对字段上设置的数据类型设置验证。

例如,我们有私有整数字段age,我们应该允许正值,因为age不能为负数。

我们可以通过使用getter和setter以及使用属性两种方式来做到这一点。

 Using Getter and Setter
// fieldprivate int _age;
// setterpublic void set(int age){if (age <=0)throw new Exception();
this._age = age;}
// getterpublic int get (){return this._age;}
Now using property we can do the same thing. In the value is a key word
private int _age;
public int Age{get{return this._age;}
set{if (value <= 0)throw new Exception()}}

自动实现属性如果我们不在get和set访问器中使用逻辑,我们可以使用自动实现属性。

当use自动实现的属性编译创建一个私有的匿名字段时,只能通过get和set访问。

public int Age{get;set;}

抽象属性抽象类可能有一个抽象属性,应该在派生类中实现

public abstract class Person{public abstract string Name{get;set;}public abstract int Age{get;set;}}
// overriden something like this// Declare a Name property of type string:public override string Name{get{return name;}set{name = value;}}

我们可以私自设置一个属性在这里,我们可以私下设置自动属性(在类中设置)

public int MyProperty{get; private set;}

您可以使用此代码实现相同的目的。在此属性设置功能不可用,因为我们必须直接将值设置为字段。

private int myProperty;public int MyProperty{get { return myProperty; }}

想想看:你有一个房间和一扇门可以进入这个房间。如果你想检查谁进来并保护你的房间,那么你应该使用属性,否则它们就不会是任何门,每个人都很容易在没有任何规则的情况下进来

class Room {public string sectionOne;public string sectionTwo;}
Room r = new Room();r.sectionOne = "enter";

人们很容易进入第一区,没有任何检查

class Room{private string sectionOne;private string sectionTwo;
public string SectionOne{get{return sectionOne;}set{sectionOne = Check(value);}}}
Room r = new Room();r.SectionOne = "enter";

现在你检查了这个人,知道他是否有邪恶的东西

绝大多数情况下,它将是您访问的属性名称,而不是变量名称(领域)原因是它被认为是. NET和C#中的良好实践,特别是保护类中的每一条数据,无论是实例变量还是静态变量(类变量),因为它与类相关联。

使用相应的属性保护所有这些变量,这些属性允许您定义设置和获取访问器,并在操作这些数据时进行验证等操作。

但在其他情况下,如数学课(系统命名空间),类中内置了几个静态属性。其中之一是数学常量PI

Math.PI

因为PI是一个定义良好的数据,我们不需要有多个PI副本,它总是相同的值。所以静态变量有时用于在类的对象之间共享数据,但它也常用于常量信息,你只需要一份数据的副本。

字段是类中的变量。字段是您可以通过使用访问修饰符封装的数据。

属性与字段类似,因为它们定义状态和与对象关联的数据。

与字段不同,属性有一种特殊的语法来控制一个人如何读取数据和写入数据,这些被称为get和set运算符。集合逻辑通常可用于进行验证。

基本和一般的区别是:

字段

  • 一直都是同时提供get和set访问权限
  • 不能导致副作用(抛出异常,调用方法,更改除获取/设置的字段之外的字段等)

属性

  • 不总是这样同时提供get和set访问权限
  • 可以导致副作用

虽然字段和属性看起来彼此相似,但它们是两个完全不同的语言元素。

  1. 字段从概念上讲是类作用域内的变量,如果你想把一些数据存储到你的类(对象)的实例中,你需要使用字段,属性不能存储任何数据,虽然属性不能存储任何数据,但看起来他们可以这样做。见下文。

  2. 属性只是方法对(get和set),可以在语法上以类似字段的方式调用,并且在大多数情况下它们访问(用于读取或写入)字段,这是一些混淆的根源。但是因为属性方法是(有一些限制,如固定原型)常规C#方法,它们可以做常规方法能做的任何事情。这意味着它们可以有1000行代码,它们可以抛出异常,调用其他方法,甚至可以是虚的、抽象的或覆盖的。使属性特别的是C#编译器将一些额外的元数据存储到可用于搜索特定属性的程序集中-广泛使用的功能。

获取和设置属性方法具有以下原型。

PROPERTY_TYPE get();
void set(PROPERTY_TYPE value);

因此,这意味着可以通过定义一个字段和2个相应的方法来“模拟”属性。

class PropertyEmulation{private string MSomeValue;
public string GetSomeValue(){return(MSomeValue);}
public void SetSomeValue(string value){MSomeValue=value;}}

这种属性模拟对于不支持属性的编程语言是典型的——比如标准C++。在C#中,您应该始终选择属性作为访问字段的方式。

因为只有字段可以存储数据,这意味着类包含的字段越多,该类的对象将消耗更多的内存。另一方面,向类中添加新属性不会使该类的对象变大。这是例子。

class OneHundredFields{public int Field1;public int Field2;...public int Field100;}
OneHundredFields Instance=new OneHundredFields() // Variable 'Instance' consumes 100*sizeof(int) bytes of memory.
class OneHundredProperties{public int Property1{get{return(1000);}set{// Empty.}}
public int Property2{get{return(1000);}set{// Empty.}}
...
public int Property100{get{return(1000);}set{// Empty.}}}
OneHundredProperties Instance=new OneHundredProperties() // !!!!! Variable 'Instance' consumes 0 bytes of memory. (In fact a some bytes are consumed becasue every object contais some auxiliarity data, but size doesn't depend on number of properties).

虽然属性方法可以做任何事情,但在大多数情况下,它们是访问对象字段的一种方式。如果你想让其他类可以访问一个字段,你可以通过两种方法来做到。

  1. 将字段公开-不可取。
  2. 使用属性。

这是一个使用公共字段的类。

class Name{public string FullName;public int YearOfBirth;public int Age;}
Name name=new Name();
name.FullName="Tim Anderson";name.YearOfBirth=1979;name.Age=40;

虽然该代码完全有效,但从设计的角度来看,它有几个缺点。因为字段既可以读取也可以写入,所以你不能阻止user写入字段。你可以应用readonly关键字,但这样,你必须仅在构造函数中初始化只读字段。更重要的是,没有什么可以阻止你将无效值存储到字段中。

name.FullName=null;name.YearOfBirth=2200;name.Age=-140;

代码是有效的,所有的赋值都将被执行,尽管它们是不合逻辑的。Age是负值,YearOfBirth是遥远的未来,与Age不对应,FullName是null。使用字段,您无法防止class Name的用户犯此类错误。

这是一个具有修复这些问题的属性的代码。

class Name{private string MFullName="";private int MYearOfBirth;
public string FullName{get{return(MFullName);}set{if (value==null){throw(new InvalidOperationException("Error !"));}
MFullName=value;}}
public int YearOfBirth{get{return(MYearOfBirth);}set{if (MYearOfBirth<1900 || MYearOfBirth>DateTime.Now.Year){throw(new InvalidOperationException("Error !"));}
MYearOfBirth=value;}}
public int Age{get{return(DateTime.Now.Year-MYearOfBirth);}}
public string FullNameInUppercase{get{return(MFullName.ToUpper());}}}

类的更新版本具有以下优点。

  1. FullNameYearOfBirth被检查为无效值。
  2. Age不可写。它是从YearOfBirth和当前年份计算的。
  3. 新属性FullNameInUppercaseFullName转换为大写。这是一个人为的属性用法示例,其中属性通常用于以更适合用户的格式呈现字段值-例如在DateTime格式的特定数字上使用当前语言环境。

除此之外,属性可以定义为虚拟或重写-仅仅因为它们是常规的。NET方法。与常规方法相同的规则适用于此类属性方法。

C#还支持索引器,这些索引器是在属性方法中具有索引参数的属性。

class MyList{private string[]                 MBuffer;
public MyList(){MBuffer=new string[100];}
public string this[int Index]{get{return(MBuffer[Index]);}set{MBuffer[Index]=value;}}}
MyList   List=new MyList();
List[10]="ABC";Console.WriteLine(List[10]);

由于C#3.0允许您定义自动属性。这是一个例子。

class AutoProps{public int Value1{get;set;}
public int Value2{get;set;}}

即使class AutoProps只包含属性(或者看起来像),它也可以存储2个值,并且此类对象的大小等于sizeof(Value1)+sizeof(Value2)=4+4=8个字节。

这样做的原因很简单。当您定义一个自动属性时,C#编译器会生成包含隐藏字段的自动代码和一个具有访问此隐藏字段的属性方法的属性。这是编译器生成的代码。

这是ILSpy从编译程序集中生成的代码。类包含生成的隐藏字段和属性。

internal class AutoProps{[CompilerGenerated][DebuggerBrowsable(DebuggerBrowsableState.Never)]private int <Value1>k__BackingField;
[CompilerGenerated][DebuggerBrowsable(DebuggerBrowsableState.Never)]private int <Value2>k__BackingField;
public int Value1{[CompilerGenerated]get{return <Value1>k__BackingField;}[CompilerGenerated]set{<Value1>k__BackingField = value;}}
public int Value2{[CompilerGenerated]get{return <Value2>k__BackingField;}[CompilerGenerated]set{<Value2>k__BackingField = value;}}}

因此,如您所见,编译器仍然使用字段来存储值——因为字段是将值存储到对象中的唯一方法。

正如你所看到的,尽管属性和字段有相似的用法语法,但它们是非常不同的概念。即使您使用自动属性或事件-隐藏字段也由存储真实数据的编译器生成。

如果你需要让外部世界(你的类的用户)可以访问一个字段值,不要使用公共或受保护的字段。字段总是应该被标记为私有的。属性允许你进行值检查、格式化、转换等,通常会使你的代码更安全、更可读、更可扩展,以便将来修改。

差异已经清楚地解释了这里。但是,只是为了总结和强调:

字段被封装在类内部用于内部操作,而属性除了用于链接共享中显示的其他内部操作外,还可用于将类暴露给外部世界。

例如:

您可以在asp.net页面内的用户控件下方运行,只需为aspx页面中的Id控制权限分配一个值,如下所示:

useMeId.Id=5 ---call the property of user control "UseMe.ascx"

UseMe.ascx

<%@ Register Src=~/"UseMe.ascx" TagPrefix="uc" TagName="UseMe" %><uc:UseMe runat="Server" id="useMeId" />
UseMe.ascx.cs
private int currentId;
public int Id{get{return currentId;}set{currentId = value;LoadInitialData(currentId);}}Private void LoadinitialData(int currentIdParam){//your action
}

阅读所有答案后,我没有找到关于并发访问的答案。

假设您有一个可以异步访问的API端点,并且您正在使用静态字段来存储数据,并且需要对静态字段进行独占访问。

要重现此示例,您需要进行负载测试以同时对端点进行大量访问。

当使用静态整型计数器字段时,端点在两次或多次访问中获得相同的值。输入图片描述

当使用静态整型计数器属性时,端点处理并发并始终获取计数器的新值。

输入图片描述

这根本没有回答这个问题,但在使用一个或另一个时必须考虑到这种行为。

简单描述:

领域是你的手,财产是你的手套,隐藏你的肤色,而它就像你的手。

你可能实际上有4个手指,但你的手套里有一个假手指,显示你是一个5手指的人。