首页 文章

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

提问于
浏览
889

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

30 回答

  • 1

    MSDN上的这个页面有一个比较和提示,在以下情况下使用哪一个:

    https://msdn.microsoft.com/en-us/library/9d65as2e(v=vs.90).aspx

  • 2

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

  • 44

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

    属性类似于Fields,它们定义状态和与对象关联的数据 .

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

  • 25

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

    例如,让我们使用名为 Employee 的类,其中包含name,age和Employee_Id的私有字段 . 我们无法从类外部访问这些字段,但我们可以通过属性访问这些私有字段 .

    我们为什么要使用属性?

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

    为了通过一个例子清楚地理解这一点,我们可以选择一个有ID,密码,姓名的学生班 . 现在在这个例子中公共字段存在一些问题

    • ID不应该是-ve .

    • 名称不能设置为null

    • 通行证应该是只读的 .

    • 如果学生姓名缺失,则不应返回姓名 .

    要删除此问题,我们使用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;
        }
    }
    
  • 2

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

  • 1

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

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

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

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

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

  • 5

    来自维基百科 - Object-oriented programming

    面向对象编程(OOP)是一种基于“对象”概念的编程范例,“对象”是包含数据的数据结构,以字段的形式,通常称为属性;代码,以程序的形式,通常称为方法 . (重点补充)

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

  • 39

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

  • 1

    在绝大多数情况下,它将是您访问的属性名称而不是变量名称( field ) . 原因是's considered good practice in .NET and in C# in particular to protect every piece of data within a class, whether it'是实例变量或静态变量(类变量),因为它与类相关联 .

    使用相应的属性保护所有这些变量,这些属性允许您在操作这些数据时定义 set and get accessors并执行验证等操作 .

    但在其他情况下,例如Math class(系统命名空间),类中内置了一些静态属性 . 其中一个是数学常数PI

    例如 . Math.PI

    由于PI是一个定义明确的数据,因此我们不需要拥有PI的多个副本,它总是具有相同的值 . 因此,静态变量有时用于在类的对象之间共享数据,但它们也常用于常量信息,其中您只需要一个数据的副本 .

  • 1

    使用Properties,可以在更改属性值(也就是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);
       }
     }
    }
    
  • 791

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

    1. Properties allows you to set the read-only access level

    考虑 dataTable.Rows.CountdataTable.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. Properties in PropertyGrid

    您可能在Visual Studio中使用了 Button . 它的属性显示在 PropertyGridTextName 等 . 当我们拖放按钮时,当我们单击属性时,它会自动找到类 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 . 为什么???因为Properties可以接受Attributes . 如果 [Browsable(false)] 为假,则不显示 .

    3. Can execute statements inside 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. Only Properties can be used in Binding Source

    Binding Source帮助我们减少代码行数 . BindingSource 不接受 Fields . 我们应该使用 Properties .

    5. Debugging mode

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

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

    面向对象的编程原则说,一个类的内部工作应该隐藏在外部世界之外 . 如果您公开一个字段,那么您实际上是暴露了该类的内部实现 . 因此,我们使用Properties(或Java的方法中的方法)来包装字段,以使我们能够在不破坏代码的情况下更改实现,具体取决于我们 . 看到我们可以在属性中放置逻辑也允许我们在需要时执行验证逻辑等 . C#3有可能令人困惑的autoproperties概念 . 这允许我们简单地定义Property,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
    }
    
  • 2

    属性封装字段,因此您可以对要设置或检索的值执行其他处理 . 如果您不对字段值进行任何预处理或后处理,则使用属性通常会过度 .

  • 1

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

    例:

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

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

  • 9

    我对一个字段的设计是一个字段只需要由它的父元素修改,因此需要修改它 . 结果变量变为私有,然后为了能够在外面读取类/方法的权限,我只通过属性系统来获取 . 然后该属性由该属性检索并且只读!如果你想修改它,你必须通过方法(例如构造函数),我发现,由于这种方式使你安全,我们可以更好地控制我们的代码,因为我们“法兰” . 人们很可能总是将所有内容都公开,因此每个可能的情况,变量/方法/类等的概念......在我看来只是对代码开发和维护的帮助 . 例如,如果一个人恢复具有公共字段的代码,他可以做任何事情,因此与目标相关的事情“不合逻辑”,代码编写原因的逻辑 . 这是我的观点 .

    当我使用经典模型私有领域/公共只读属性时,对于10个私有领域,我应该写10个公共属性!代码可以更快 . 我发现私人二传手,现在我只使用私人二传手的公共 property . setter在后台创建一个私有字段 .

    That why my old classic programming style was:

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

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

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

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

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

    我们可以使用getter和setter以及使用属性以两种方式执行此操作 .

    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属性如果我们在get和set访问器中没有逻辑,我们可以使用自动实现的属性 .

    当你只能通过get和set访问器访问时_99424 .

    public int Age{get;set;}
    

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

    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属性(在类中设置)

    public int MyProperty
    {
        get; private set;
    }
    

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

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

    想一想:你有一个房间和一扇门进入这个房间 . 如果你想检查谁进入并保护你的房间,那么你应该使用属性,否则他们将不会是任何门,每个人都很容易进入没有任何规定

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

    Now you checked the person and know about whether he has something evil with him

  • 8

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

  • 135

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

  • 87

    当你有一个 class 是“汽车” . 属性是颜色,形状..

    其中,字段是在类的范围内定义的变量 .

  • 2

    字段是 ordinary member variables 或类的成员实例 . 属性是 abstraction to get and set their values . 属性也称为访问器,因为如果将类中的字段公开为私有,它们提供了更改和检索字段的方法 . 通常,您应该将成员变量声明为private,然后为它们声明或定义属性 .

    class SomeClass
      {
         int numbera; //Field
    
         //Property 
        public static int numbera { get; set;}
    
      }
    
  • 2

    我将举几个使用可能使齿轮转动的属性的例子:

    • Lazy Initialization: 如果在正常运行的代码中有's expensive to load, but isn' t访问过的对象的属性,则可以通过属性延迟加载 . 这样,它只是坐在那里,但是第一次另一个模块试图调用该属性时,它会检查底层字段是否为空 - 如果是,则继续并加载它,调用模块不知道 . 这可以大大加快对象初始化 .

    • Dirty Tracking: 我从StackOverflow上的own question实际了解到了这一点 . 当我有很多对象在运行期间可能已更改的值时,我可以使用该属性来跟踪是否需要将它们保存回数据库 . 如果没有对象的单个属性发生更改,则IsDirty标志不会被触发,因此在决定需要返回数据库时,保存功能将跳过它 .

  • 4

    如果希望私有变量(字段)可以从其他类访问类的对象,则需要为这些变量创建属性 .

    例如,如果我有名为“id”和“name”的变量是私有的,但可能存在这种变量在类之外进行读/写操作所需的情况 . 在这种情况下,属性可以帮助我根据为属性定义的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";
        }
    }
    
  • 1

    这里的第二个问题,"when should a field be used instead of a property?",只是在this other answer中有所简单,有点this one too,但并不是很详细 .

    一般来说,所有其他答案都是关于良好设计的重点:更喜欢在暴露字段上暴露属性 . 虽然你可能不会 regularly 发现自己在说"wow, imagine how much worse things would be if I had made this a field instead of a property",但考虑到你会说"wow, thank God I used a field here instead of a property."的情况更为罕见 so much

    但是字段具有超过属性的一个优点,那就是它们被用作“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);
    }
    

    自己做一些measurements,带有字段的版本大约有61%的时间用作带有属性的版本(.NET 4.6,Windows 7,x64,发布模式,没有连接调试器) . TransformPoint 方法越昂贵,差异变得越不明显 . 要自己重复此操作,请在第一行注释掉并且没有注释掉 .

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

    我绝对不希望看起来像是在鼓吹你“哦,我应该开始暴露场而不是属性 . ”关键是,如果您需要在接听“ref”或“out”的电话中定期使用这些成员参数,特别是对于可能是一个不太可能需要任何属性的增值元素的简单值类型的东西,可以进行参数化 .

  • 1

    属性支持非对称访问,即您可以使用getter和setter,也可以只使用其中一个 . 类似地,属性支持getter / setter的单个可访问性 . 字段始终是对称的,即您始终可以获取和设置值 . 对此的例外是readonly字段,显然在初始化后无法设置 .

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

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

    属性通过封装获取/设置值的任何方式来支持抽象级别 .

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

  • 1

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

  • 0

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

    所以你可以说你只想设置一个整数字段的值,如果值大于x,否则抛出异常 .

    真有用的功能 .

  • 1

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

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

  • 1

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

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

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

  • 1

    属性的主要优点是允许您更改对象上的数据访问方式,而不会破坏它的公共接口 . 例如,如果您需要添加额外的验证,或者将存储的字段更改为计算结果,则可以在最初将该字段作为属性公开时轻松完成 . 如果您只是直接暴露了一个字段,那么您必须更改类的公共接口以添加新功能 . 该更改将破坏现有客户端,要求在他们使用新版本的代码之前重新编译它们 .

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

相关问题