C#中的字段和属性有什么区别?
在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 accessed it uses the underlying field, // but only exposes the contract, which will not be affected by the underlying field public 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 you public int AnotherProperty{get;set;} }
@Kent指出,属性不需要封装字段,他们可以在其他字段上进行计算,或用于其他目的。
@GSS指出,当一个属性被访问时,你也可以做其他的逻辑,如validation,另一个有用的function。
面向对象的编程原则指出,一个class级的内部运作应该从外部隐藏起来。 如果你暴露了一个领域,你实际上暴露了这个类的内部实现。 因此,我们用Properties(或者Java中的方法)来包装字段,使我们能够改变实现而不破坏取决于我们的代码。 看到我们可以把财产的逻辑也允许我们执行validation逻辑等,如果我们需要它。 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 }
一个重要的区别是接口可以有属性而不是字段。 对我来说,这强调了应该使用属性来定义一个类的公共接口,而字段是为了在一个类的私人内部工作中使用。 通常我很less创build公共领域,同样我也很less创build非公有财产。
我会给你几个使用可能会使齿轮转动的属性的例子:
- 延迟初始化 :如果你有一个对象的属性是一个昂贵的对象,但是在代码的正常运行中没有被访问,那么你可以通过属性来延迟它的加载。 这样,它只是坐在那里,但是第一次另一个模块试图调用该属性时,它会检查基础字段是否为空 – 如果是,则继续并加载它,调用模块不知道。 这可以大大加快对象的初始化。
- 肮脏的跟踪:我实际上从我自己的问题在这里了解StackOverflow。 当我有很多对象的值可能在运行过程中发生了变化,我可以使用该属性来跟踪是否需要将其保存回数据库。 如果没有对象的单个属性发生变化,则IsDirty标志不会被触发,因此在确定需要返回到数据库的时候,保存function将跳过它。
使用属性,你可以抛出一个事件,当属性的值被改变(又名。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); } } }
由于他们中的许多人已经解释了Properties
和Field
技术优劣,现在是时候进入实时的例子了。
1.属性允许您设置只读访问级别
考虑dataTable.Rows.Count
和dataTable.Columns[i].Caption
。 他们来自DataTable
类,都是公开给我们的。 访问级别的区别在于我们无法将值设置为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, 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,当我们点击属性时,它会自动find类Button
和过滤Properties
并显示在PropertyGrid
(其中PropertyGrid
不会显示即使他们是公开的领域)。
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
。 为什么??? 因为属性可以接受属性 。 如果[Browsable(false)]
为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 _count return _count; } }
4.只有属性可以在绑定源中使用
绑定源可以帮助我们减less代码行数。 BindingSource
不接受这些Fields
。 我们应该使用Properties
。
5.debugging模式
考虑我们使用Field
来保存一个值。 在某个时候,我们需要debugging并检查该字段的值为空。 在代码行数超过1000的情况下很难做到。在这种情况下,我们可以使用Property
并且可以在Property
设置debugging模式。
public string Name { // Can set debug mode inside get or set get { return _name; } set { _name = value; } }
差异 – 使用(何时和为什么)
字段是直接在类或结构中声明的variables。 类或结构可能有实例字段或静态字段或两者。 一般来说,您只能将字段用于具有私有或受保护可访问性的variables 。 应该通过方法,属性和索引器提供您的类暴露给客户端代码的数据。 通过使用这些结构间接访问内部字段,可以防止无效的input值。
属性是提供灵活机制来读取,写入或计算专用字段的值的成员。 属性可以像使用公共数据成员一样使用,但实际上它们是称为访问器的特殊方法。 这使得数据可以被轻松访问,仍然有助于提高方法的安全性和灵活性 。 属性使类能够公开获取和设置值的方式,同时隐藏实现或validation码。 get属性访问器用于返回属性值,并使用set访问器来分配新值。
属性的主要优势在于允许您更改访问对象上的数据的方式,而不会破坏它的公共接口。 例如,如果您需要添加额外的validation,或者将存储的字段更改为计算结果,那么如果您最初将该字段作为属性公开,则可以轻松完成。 如果你直接暴露了一个字段,那么你将不得不改变你的类的公共接口来添加新的function。 这种改变会破坏现有的客户端,要求他们在使用新版本的代码之前重新编译它们。
如果你编写一个为广泛使用而devise的类库(比如数百万人使用的.NET Framework),这可能是个问题。 但是,如果你正在编写一个在小代码库内部使用的类(比如<= 50 K行),这实际上不是什么大问题,因为没有人会受到你的改变的不利影响。 在这种情况下,这实际上归结为个人偏好。
属性支持不对称的访问,即你可以有一个getter和一个setter或者只是其中的一个。 类似的属性支持getter / setter的单独的可访问性。 字段总是对称的,即你总是可以获取和设置值。 例外是只读字段,显然不能在初始化之后设置。
属性可能会运行很长时间,有副作用,甚至可能会抛出exception。 字段速度快,没有副作用,绝不会抛出exception。 由于副作用,属性可能会为每个调用返回一个不同的值(可能是DateTime.Now的情况,即DateTime.Now并不总是等于DateTime.Now)。 字段总是返回相同的值。
字段可能用于out / ref参数,属性可能不会。 属性支持额外的逻辑 – 这可以用来实现延迟加载等等。
属性支持一个抽象级别,通过封装它的意思来获得/设置值。
在大多数情况下使用属性,但是尽量避免副作用。
在后台,一个属性被编译成方法。 所以一个Name
属性被编译成get_Name()
和set_Name(string value)
。 你可以看到这个,如果你研究编译的代码。 所以在使用它们时会有(非常)小的性能开销。 通常情况下,如果您向外部公开某个字段,您将始终使用一个属性,如果您需要validation该值,则通常会在内部使用它。
当你希望你的私有variables(字段)可以从其他类的类的对象访问时,你需要为这些variables创build属性。
例如,如果我有variables名为“ID”和“名称”这是私人的,但可能会有这种情况,这个variables需要读/写操作以外的类。 在这种情况下,属性可以帮助我根据为属性定义的get / set来读取/写入该variables。 属性可以是只读/只写/读写。
这里是演示
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"; } }
这里的第二个问题,“什么时候应该使用一个字段而不是一个属性?”,在这个其他答案中只是简要地提到了这个问题 ,而且也没有涉及到这个问题 。
一般来说,所有其他的答案都是关于优秀devise的重点:喜欢暴露暴露域的属性。 虽然你可能不会经常发现自己说:“哇,想象一下,如果我把这个领域变成了一个地产而不是一个地产,情况会变得更糟糕”,但是想一想你会说“哇,感谢上帝,我在这里使用了一个场,而不是一个财产。“
但是有一个优点,就是字段的属性超过了属性,这就是它们被用作“ref”/“out”参数的能力。 假设你有一个具有以下签名的方法:
public void TransformPoint(ref double x, ref double y);
并假设你想用这个方法来转换一个像这样创build的数组:
System.Windows.Point[] points = new Point[1000000]; Initialize(points);
这是我认为最快的方法,因为X和Y是属性:
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; }
这将是非常好的! 除非你有另外的证据certificate,否则没有任何理由会发出恶臭。 但是我相信在技术上没有这么快的保证:
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); }
自己做一些测量 ,包含字段的版本需要约61%的时间作为版本的属性(.NET 4.6,Windows 7,x64,发布模式,没有附加的debugging器)。 TransformPoint
方法获得的成本越高,差异变得越不明显。 要自己重复这一点,请将第一行注释掉,不要注释掉。
即使没有性能上的好处,还有其他地方可以使用ref和out参数可能是有益的,例如调用Interlocked或Volatile方法时。 注意:如果这对你来说是新的,那么Volatile基本上是一种获得volatile
关键字提供的相同行为的方法。 因此,就像volatile
一样,它不会神奇地解决所有线程安全的问题,就像它的名字所暗示的那样。
我绝对不希望看起来像我主张你走“哦,我应该开始暴露领域而不是属性”。 关键在于,如果您需要经常使用带有“ref”或“out”参数的调用中的这些成员,特别是在某些可能是简单的值types的东西中,这些东西不太可能需要任何属性的增值元素,可以提出一个论点。
此外,属性允许您在设置值时使用逻辑。
所以你可以说你只想设置一个整数字段的值,如果该值大于x,则抛出exception。
真正有用的function。
如果你打算使用线程原语,你不得不使用字段。 属性可以破坏你的线程代码。 除此之外,科里所说的是正确的。
(这应该是一个评论,但我不能发表评论,所以请原谅,如果它不适合作为一个职位)。
我曾经在一个推荐的做法是使用公有领域而不是属性的地方工作,而等价的属性def只是访问一个领域,如:
get { return _afield; } set { _afield = value; }
他们的推理是,如果需要的话,公共领域可以在将来的将来转化为财产。 当时对我来说似乎有些奇怪。 从这些post来看,这里看起来并不多。 你可能会说什么来改变事情呢?
编辑:我应该补充说,在这个地方所有的代码库都是在同一时间编译的,所以他们可能会认为改变类的公共接口(通过改变一个公共字段)是不成问题的。
MSDN上的这个页面有一个比较和提示,在下列情况下使用哪一个:
https://msdn.microsoft.com/en-us/library/9d65as2e(v=vs.90).aspx
字段是普通的成员variables或类的成员实例。 属性是获取和设置其值的抽象 。 属性也称为访问器,因为如果将类中的字段暴露为私有属性,则它们提供了更改和检索字段的方法。 一般来说,你应该声明你的成员variables是私有的,然后声明或定义它们的属性。
class SomeClass { int numbera; //Field //Property public static int numbera { get; set;} }
属性封装字段,从而使您可以对要设置或检索的值执行附加处理。 如果不对字段值进行任何预处理或后处理,那么使用属性通常是矫枉过正的。
从技术上讲,我不认为有什么区别,因为属性只是围绕用户创build的字段或由编译器自动创build的包装器。属性的目的是强制封装,并提供一个轻量级的类似方法的function。 将字段声明为公共是一个不好的做法,但是没有任何问题。
IMO,属性只是我们以前使用的“SetXXX()”“GetXXX()”函数/方法/接口对,但它们更简洁优雅。
传统上私人领域是通过getter和setter方法设置的。 为了减less代码,您可以使用属性来设置字段。
当你有一个类是“汽车”。 属性是颜色,形状..
字段是在类范围内定义的variables。
维基百科 – 面向对象编程 :
面向对象程序devise(OOP)是一种基于“对象”概念的编程范例,它是以字段的forms包含数据的数据结构,通常称为属性; 和程序的forms,通常被称为方法 。 (强调加)
属性实际上是对象行为的一部分,但其目的是为对象的消费者提供处理对象数据的错觉/抽象。
属性是特殊的类成员,在属性中,我们使用预定义的Set或Get方法。他们使用访问器来读取,写入或更改私有字段的值。
例如,让我们带一个名为Employee
的类,其中包含名称,年龄和Employee_Id的私有字段。 我们无法从课堂外访问这些字段,但是我们可以通过属性访问这些私有字段。
为什么我们使用属性?
公开和暴露这个类的领域是有风险的,因为你不能控制什么被分配和返回。
以一个例子来清楚地理解这一点让我们来带一个有ID,passmark,名字的学生课。 现在在这个例子中公共领域的一些问题
- ID不应该是-ve。
- 名称不能设置为空
- 合格标记应该只读。
- 如果学生姓名不存在,则不应返回姓名。
为了消除这个问题我们使用Get和set方法。
// A simple example public 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 -ve s1.Name = null ; // here Name can't be null } }
现在我们以get和set方法为例
public class student { private int _ID; private int _passmark; private string_name ; // for id property public 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 property public 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 property public int Getpassmark() { return this._passmark; } }
我的一个领域的devise是一个领域只需要修改它的父母,因此类。 结果variables变成私有的,然后才能赋予权限去读取我只通过Get的属性系统之外的类/方法。 该字段然后由该属性检索并且是只读的! 如果你想修改它,你必须通过方法(例如构造函数),我发现,由于这种方式使你的安全,我们有更好的控制我们的代码,因为我们“法兰”。 人们总是可以把所有东西都公诸于众,所以每一种可能的情况下,variables/方法/类的概念等……在我看来,只是对代码开发和维护的一种帮助。 例如,如果一个人恢复公共领域的代码,他可以做任何事情,因此与目标相关的事情是“不合逻辑的”,这就是代码编写的逻辑。 这是我的观点。
当我使用一个经典的模型私人领域/公共只读属性,10私人领域,我应该写10个公共属性! 代码可以更快。 我发现私人二传手,现在我只使用私人二传手的公共财产。 制定者在后台创build一个私人领域。
那为什么我的旧经典编程风格是:
public class MyClass { private int _id; public int ID { get { return _id; } } public MyClass(int id) { _id = id; } }
My new programming style:
public class MyClass { public int ID { get; private set; } public MyClass(int id) { ID = id; } }
Additional info: By default, get and set accessors are as accessible as the property itself. You can control/restrict accessor accessibility individually (for get and set) by applying more restrictive access modifiers on them.
例:
public string Name { get { return name; } protected set { name = value; } }
Here get is still publicly accessed (as the property is public), but set is protected (a more restricted access specifier).
Properties are used to expose field. They use accessors(set, get) through which the values of the private fields can be read, written or manipulated.
Properties do not name the storage locations. Instead, they have accessors that read, write, or compute their values.
Using properties we can set validation on the type of data that is set on a field.
For example we have private integer field age on that we should allow positive values since age cannot be negative.
We can do this in two ways using getter and setters and using property.
Using Getter and Setter // field private int _age; // setter public void set(int age){ if (age <=0) throw new Exception(); this._age = age; } // getter public 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() } }
Auto Implemented property if we don't logic in get and set accessors we can use auto implemented property.
When u se auto-implemented property compiles creates a private, anonymous field that can only be accessed through get and set accessors.
public int Age{get;set;}
Abstract Properties An abstract class may have an abstract property, which should be implemented in the derived class
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; } }
We can privately set a property In this we can privately set the auto property(set with in the class)
public int MyProperty { get; private set; }
You can achieve same with this code. In this property set feature is not available as we have to set value to field directly.
private int myProperty; public int MyProperty { get { return myProperty; } }
Think about it : You have a room and a door to enter this room. If you want to check how who is coming in and secure your room, then you should use properties otherwise they won't be any door and every one easily come in w/o any regulation
class Room { public string sectionOne; public string sectionTwo; } Room r = new Room(); r.sectionOne = "enter";
People is getting in to sectionOne pretty easily, there wasn't any checking
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";
Now you checked the person and know about whether he has something evil with him