字段和属性之间有什么区别?


Answers:


979

属性公开字段。字段(几乎总是)应该对类保持私有,并可以通过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 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指出,不需要Properties来封装字段,它们可以对其他字段进行计算或用于其他目的。

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


185
值得注意的是,封装字段不需要属性。物业后面可能根本没有任何场地。它可能是一个计算或返回一个常数或其他任何值。
肯特·布加亚特

9
“同时不影响使用类的事物访问它们的外部方式。” 如果我理解不正确,请原谅我,但是,如果属性后面的字段似乎可以解决这个问题,为什么还要在属性前面完全需要访问修饰符呢?也就是说,为什么要公开财产以外的其他东西?
2014年

18
您的答案就在修改之前,并且奇怪地提出了不正确的评论。属性应始终封装一个或多个字段,并且绝不应进行任何繁重的工作或验证。如果您需要诸如UserName或Password这样的属性来进行验证,请将其类型从字符串更改为Value Objects。类创建者和消费者之间有一个不言而喻的契约。字段保持状态,属性使用一个或多个字段公开状态,虚空更改状态(繁重的提升)和函数执行查询(繁重的提升)。
Suamere 2015年

6
@jpaugh如果我是班级消费者,则应遵循班级创建者设定的合同。如果一个属性是string,则我的合同是:分配长度不超过20亿的任何字符。如果一个属性是DateTime,则我的合同是:在我可以查询的DateTime的范围内分配任何数字。如果创建者向设置者添加了约束,则不会传达这些约束。但是,如果创建者将类型从更改stringSurname,则其新的Surname类传达约束,并且该属性将public Surname LastName没有setter验证。而且,Surname是可重用的。
Suamere '17

4
而且,由于Surname在我的示例中是可重用的,因此您以后不必担心将那些在属性设置器中的验证复制/粘贴到代码中的其他位置。同样,如果您曾经更改过姓氏的业务规则,也不会怀疑姓氏的验证是否在多个地方。看看我张贴了关于值对象的链接
Suamere

261

面向对象的编程原理说,一个类的内部工作应该对外界隐藏。如果公开一个字段,则本质上就是公开该类的内部实现。因此,我们使用属性(或Java中的方法)包装字段,以使我们能够更改实现而不必依赖我们的代码。看起来我们可以将逻辑放入属性中,这也使我们能够在需要时执行验证逻辑等。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
}

89
+1代表自动属性-我认为这是这里(及其他地方)很多答案都遗忘的东西。没有这种解释,仍然很难理解public int myVar { get; set; }真正的含义(我想这就是原因)至少要获得此问题的50%的点击率)。
Priidu Neemre

7
+1还提到了auto,并提到了auto的工作原理(“ AutoProperty为我们生成了私有字段”),这是我一直在寻找的答案。在研究时,我在MSDN的页面上没有看到有关它们的任何迹象,表明创建了私有字段并引起混乱。我想那是什么意思?“属性允许在自动实现的属性上使用,但显然不允许在后备字段上使用,因为不能从源代码访问这些属性。如果必须在属性的后备字段上使用属性,则只需创建一个常规属性即可。” 但不确定。
尼拉,2014年

3
请注意,给定的示例未封装蹲坐。此属性为私有字段提供100%的完全访问权限,因此这根本不是面向对象的。在这种情况下,您最好有一个公共场所。当然,它有助于将来(少量地)重构代码,但是任何值得的IDE都可以通过几次击键将字段转换为属性。对于属性的工作方式,从技术上来说,答案可能是正确的,但是对于它们的使用并不能给出很好的“ OOP解释”。
sara 2016年

2
@kai我同意答案过于简单,并没有显示出自动属性的所有功能,但是我不同意这不是面向对象的。您可能需要检查字段和属性之间的区别。字段不能是虚拟的,virtual它本身是面向对象编程的一部分。
戈贝'02

当然,有一些功能上的区别。我自己不会称呼virtualOOP。这是一个启用多态性的工具,这是启用OOP的关键工具之一。但是,它本身并不是面向对象的,并且关于公共自动财产的本质上没有面向对象的。我也不会考虑诸如反射或与OOP相关的数据绑定之类的东西。通常,我不会对此过于so腐,但是答案特别提到了OO原则作为代码示例背后的推动力,我对此并不认同。
萨拉

164

一个重要的区别是接口可以具有属性,但不能具有字段。对我而言,这强调了应使用属性来定义类的公共接口,而字段则应在类的私有内部工作中使用。通常,我很少创建公共字段,并且类似地,我很少创建非公共属性。


98

我将为您提供一些使用属性的示例,这些属性可能会导致齿轮旋转:

  • 延迟初始化如果您拥有一个对象的属性,该属性的加载成本很高,但是在正常的代码运行中却没有得到太多访问,则可以通过该属性延迟其加载。这样,它就坐在那里,但是当另一个模块第一次尝试调用该属性时,它会检查基础字段是否为空-如果是,它将继续进行加载并将其装入调用方模块未知的字段。这样可以大大加快对象的初始化速度。
  • 脏跟踪:我实际上是从我在StackOverflow上的一个问题中学到的。当我有很多对象的值在运行期间可能已更改时,我可以使用该属性来跟踪是否需要将它们保存回数据库。如果对象的单个属性没有更改,则IsDirty标志不会被触发,因此,在确定需要返回数据库的内容时,保存功能将跳过它。

1
有关脏跟踪的问题:如果我可以直接更改字段怎么办–我不知道是否可以这样做,我可以说:“如果没有更改对象的单个FIELD,则无需保存对象”因此,脏跟踪不会有什么区别,我是否缺少某些东西?
网站

2
@juanpastas:关于脏跟踪的属性的优点是,如果属性设置程序将设置“脏”标志,则在未设置标志的情况下,代码无需检查任何属性的值即可查看如果他们可能已经改变。相反,如果对象将其属性公开为字段,则必须将所有字段的内容与先前的值进行比较(这不仅增加了进行比较的时间,而且还意味着代码必须具有先前的值)。
supercat

那是一个好方法。它还允许您触发方法(作为事件),或在设置或读取值时进行记录。
coloboxp

54

使用属性,可以在更改属性值(也称为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);
 }
}

3
我花了很长时间才找到这个。这是MVVM。谢谢 !:)

46

因为他们中许多人与技术的优点和缺点的解释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, 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中的属性

您可能已经Button在Visual Studio中使用过。它的属性显示在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。为什么???因为属性可以接受Attributes。万一[Browsable(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.在绑定源中只能使用属性

绑定源可以帮助我们减少代码行数。Fields不被接受BindingSource。我们应该Properties为此使用。

5.调试模式

考虑我们Field用来保存一个值。在某些时候,我们需要调试并检查该字段的值在哪里变为空。如果代码行数超过1000,将很难做到。在这种情况下,我们可以使用Property并可以在其中设置调试模式Property

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

这些都是有趣的事实,但是您缺少领域和属性哲学的意义。
David FerenczyRogožan2015年

所说的PHILISOPHY是什么意思?@Dawid Ferenczy
Sarath Avanavu

例如参见标记答案。但是您注意到,您只是在提供一个使用示例,因为已经描述了字段和属性之间的区别,所以请忘了我的评论:)
David FerenczyRogožan2015年

2
阅读我在回答中的第一句话。我已经特别告诉过我,在这里我不再重复任何事情。这是没有意义的!!!人们将首先查看描述,然后是示例。标有答案的答案可以很好地说明问题,但是我添加了一些有意义的实时方案和示例。在评论@Dawid Ferenczy
Sarath Avanavu

1
我已经阅读了它,但是您显然没有阅读我之前的评论:“ 但是您注意到,您只是在提供一个用法示例,因为已经描述了字段和属性之间的区别,所以请忘了我的评论,请:) ” 。
David FerenczyRogožan2015年

32

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

字段是直接宣布类或结构的一个变量。一个类或结构可以具有实例字段或静态字段,或两者都有。通常,应将字段用于具有私有或受保护的可访问性的变量。您的类公开给客户端代码的数据应通过方法,属性提供和索引器提供。通过使用这些结构间接访问内部字段,可以防止输入值无效。

属性是,提供了一个灵活的机制来读,写,或计算私有字段的值的构件。可以将属性当作公共数据成员使用,但实际上它们是称为accessors的特殊方法。这使数据易于访问,并且仍然有助于提高方法的安全性和灵活性。属性使类可以公开获取和设置值的公共方式,同时隐藏实现或验证代码。获取属性访问器用于返回属性值,而设置访问器用于分配新值。


这是一个很棒的答案,确实帮助我理解了这一点。
史蒂夫·鲍曼

14

属性的主要优点是允许您更改访问对象上数据的方式而不会破坏其公共接口。例如,如果您需要添加额外的验证,或者将存储的字段更改为计算所得的字段,那么如果您最初将字段公开为属性,则可以轻松实现。如果您只是直接公开一个字段,则必须更改类的公共接口以添加新功能。这种更改将破坏现有的客户端,要求他们重新编译后才能使用新版本的代码。

如果您编写了一个为广泛使用而设计的类库(例如数以百万计的人使用的.NET Framework),那可能是个问题。但是,如果您要在一个小的代码库(例如<= 5万行)内编写一个内部使用的类,那实际上就没什么大不了的,因为没有人会受到您的更改的不利影响。在那种情况下,这实际上取决于个人喜好。


11

属性支持非对称访问,即,您可以具有getter和setter或仅是两者之一。类似地,属性支持对getter / setter的个别可访问性。字段始终是对称的,即,您始终可以同时获取和设置值。唯一的例外是只读字段,在初始化后显然无法设置该字段。

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

字段可以用于out / ref参数,而属性可以不使用。属性支持其他逻辑–可以用于实现延迟加载等。

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

在大多数情况下都使用属性,但要避免产生副作用。


当字段的数据类型是带有转换运算符重载的对象时,字段可能具有属性的所有成本问题-这是一个微妙的陷阱。
安迪登特

1
属性永远不会有副作用。甚至调试器都假定它可以安全地评估它们。
Craig Gidney

@Strilanc:我完全同意,但是,并非总是如此。至于调试器,如果您要谈论的是FuncEval,则有很多问题。
布赖恩·拉斯穆森

11

在后台将属性编译为方法。因此,将Name属性编译为get_Name()set_Name(string value)。如果您研究编译的代码,则可以看到此信息。因此,使用它们时(非常)很小的性能开销。通常,如果将字段暴露在外部,则将始终使用属性;如果需要验证值,则经常在内部使用它。


7

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

例如,如果我有一个名为“ id”和“ name”的变量,它们是私有的,但是在某些情况下可能需要该变量在类外进行读/写操作。在这种情况下,property可以帮助我根据为该属性定义的get / set来获取要读取/写入的变量。一个属性可以是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";
    }
}

6

这里的第二个问题,“当应在现场使用,而不是财产?”,仅在简要介绍了这对方的回答还挺这一个了,但没有真正的细节。

总的来说,所有其他答案都是关于良好设计的问题:相对于暴露字段,更喜欢暴露属性。虽然您可能不会经常发现自己说“哇,想像一下如果我将其设置为字段而不是属性,情况会更糟”,这是这么多更为罕见想到的情况下,你会说:“哇,感谢上帝,我在这里使用的是田地,而不是财产。”

但是,字段具有优于属性的一个优点,那就是它们可以用作“ 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参数可能是有益的,例如在调用InterlockedVolatile系列方法时。 注意:如果这是您的新手,那么Volatile基本上是一种实现volatile关键字提供的相同行为的方法。因此,就像一样volatile,它不能神奇地解决所有线程安全问题,就像它的名字暗示的那样。

我绝对不希望我提倡您去“哦,我应该开始显示字段而不是属性”。关键是,如果您需要在带有“ ref”或“ out”参数的调用中定期使用这些成员,尤其是在那些可能是简单值类型,不太可能不需要属性的任何增值元素的调用上,可以争论。


6

尽管字段和属性看起来彼此相似,但是它们是2种完全不同的语言元素。

  1. 字段是如何在类级别存储数据的唯一机制。字段在概念上是类范围内的变量。如果要将一些数据存储到类(对象)的实例中,则需要使用字段。别无选择。尽管属性无法存储任何数据,但看起来它们可以存储数据。见下面。

  2. 另一方面,属性从不存储数据。它们只是一对方法(获取和设置),可以用与字段类似的方式在语法上调用它们,并且在大多数情况下,它们访问(用于读取或写入)字段,这是造成混淆的原因。但是由于属性方法是常规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;

虽然代码是完全有效的,但从设计的角度来看,它有几个缺点。由于字段可以读写,因此不能阻止用户写入字段。您可以应用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. 新属性FullNameInUppercase将转换FullName为大写。这是属性用法的一个人为设计示例,其中属性通常用于以更适合用户的格式显示字段值-例如,使用特定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;
        }
    }
}

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

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

如果您需要使外部用户(您的类的用户)可以访问字段值,请不要使用公共字段或受保护的字段。字段始终应标记为私有。属性使您可以进行值检查,格式设置,转换等,并且通常使您的代码更安全,更易读且可扩展,以便将来进行修改。


4

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

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

真正有用的功能。


4

如果要使用线程原语,则必须使用字段。属性会破坏您的线程代码。除此之外,科里所说的是正确的。


1
从何时起?将您的支持字段锁定在该属性内,这是对等的
Sekhat

1
属性是方法,今天没有任何CIL JIT内联。如果要使用诸如互锁的线程基元,则需要具有字段。检查您的来源。不可否认,“锁定”是一个错误的词。
乔纳森·C·迪金森

4

(这实际上应该是评论,但我不能发表评论,因此,如果不适合发表评论,请原谅)。

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

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

他们的理由是,如果需要,将来可以将公共场所转换为财产。当时对我来说有点奇怪。从这些帖子来看,似乎也没有多少人同意。您可能会说些什么来尝试改变事情?

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


C#3.0开始,此处描述的模式由称为自动实现的属性的功能方便地支持。
DavidRR 2014年

我认为带有属性的C#具有属性的C#的优点之一是,它们与字段具有相同的API,因此该类的客户端实际上并不关心是否访问属性或字段。(例如,在C ++中不是这样)。在原型制作中,我认为从公共字段开始然后根据需要迁移到属性是合理的。属性会影响性能和内存,还有额外的输入。他们不是免费的。但是,如果您改变主意,则无需重构任何相关代码。
马克·拉卡塔

属性不能用作OUT或REF参数,因此将字段更改为属性可能会导致编译错误。如果从一开始就将该值实现为属性,则永远不会将其用作OUT或REF(Pascal / Delphi中的VAR)参数,并且您在getter / setter中所做的任何更改对用法都是透明的。
HeartWare '17

4

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


4

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

  class SomeClass
  {
     int numbera; //Field

     //Property 
    public static int numbera { get; set;}

  }

3

属性封装字段,从而使您可以对要设置或检索的值执行其他处理。如果您将不对字段值进行任何预处理或后处理,则使用属性通常是过高的。


1
不,我总是使用属性,它使您可以随时更改实现的灵活性,而不会破坏您的API。
Sekhat

关于API的演变,您可以将字段用于私有数据而不会出现问题。同样,在某些情况下,如果您想在部件中共享数据,则可以授予字段“内部”访问权限。
Daniel Earwicker's

3

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


3

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


3

当您有一个“汽车”课程时。属性是颜色,形状。

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


3

维基百科- 面向对象编程

面向对象编程(OOP)是一种基于“对象”概念的编程范例,“对象”是包含数据的数据结构,这些数据以字段的形式出现,通常称为属性;和代码,以过程的形式,通常称为方法(添加了重点)

属性实际上是对象行为的一部分,但旨在使对象的使用者获得使用对象数据的错觉/抽象。


3

我对字段的设计是,字段仅需要由其父级(即类)修改。结果变量变为私有,然后能够赋予读取外部类/方法的权利,我仅使用Get来遍历属性系统。然后该字段由属性检索,并且为只读!如果要对其进行修改,则必须遍历方法(例如,构造函数),并且我发现,由于采用了这种确保您安全的方法,我们可以更好地控制代码,因为我们采用了“法兰连接”。我很可能总是将所有内容公开,所以在每种情况下,变量/方法/类的概念等等……在我看来都是对代码开发,维护的一种帮助。例如,如果一个人使用公共领域恢复代码,那么他可以做任何事情,因此事情“不合逻辑” 关于目标,为什么编写代码的逻辑。这是我的观点。

当我使用经典模型私有字段/公共只读属性时,对于10个私有字段,我应该编写10个公共属性!代码可以更快地完成。我发现了私人二传手,现在我只将公共财产与私人二传手一起使用。设置员在后台创建一个私有字段。

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

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

是的,我不好,对不起!
托尼·皮诺

3

考虑一下:您有一个房间,有一扇门可以进入这个房间。如果要检查谁进入房间并保护房间安全,则应使用属性,否则它们将无门可乘,每个人都容易进入而无任何规定

class Room {
   public string sectionOne;
   public string sectionTwo;
}

Room r = new Room();
r.sectionOne = "enter";

人们很容易进入sectionOne,没有任何检查

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";

现在,您检查了此人,并知道他是否有恶魔


3

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

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

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


2

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

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

为什么使用属性?

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

为了通过一个例子清楚地理解这一点,让我们参加一个具有ID,密码,姓名的学生班。现在在此示例中,公共领域存在一些问题

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

要解决此问题,我们使用Get and 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;
    }
}

2

附加信息:默认情况下,获取和设置访问器的访问权限与属性本身一样。您可以通过对它们应用更多限制性的访问修饰符来分别控制/限制访问器的可访问性(用于get和set)。

例:

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

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


2

属性用于公开字段。他们使用访问器(设置,获取),通过它们可以读取,写入或操作私有字段的值。

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

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

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

我们可以使用getter和setter以及使用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()
       }
    }

自动实现的属性,如果我们在获取和设置访问器中不加逻辑,则可以使用自动实现的属性。

当使用自动实现的属性编译时,将创建只能通过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;
     }
  }

我们可以私下设置属性,在此我们可以私下设置auto属性(在类中设置with)

public int MyProperty
{
    get; private set;
}

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

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

2

在大多数情况下,它将是您访问的属性名,而不是变量名(字段),其原因是,在.NET和C#中,这被认为是一种很好的做法,尤其是保护类中的所有数据,无论是实例变量还是静态变量(类变量),因为它都与类相关联。

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

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

例如。数学PI

而且由于PI是一条定义明确的数据,因此我们不需要具有多个PI副本,因此它始终是相同的值。因此,有时会使用静态变量在类的对象之间共享数据,但也常用于常量信息,在这种情况下,您只需要一个数据副本。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.