天天看點

字段和屬性之間有什麼差別?

在C#中,是什麼使字段不同于屬性,以及何時應使用字段代替屬性?

#1樓

(這實際上應該是評論,但我不能發表評論,是以,如果它不适合發表,請原諒)。

我曾經在一個推薦的實踐是在公共屬性而不是屬性的地方工作,而等效的屬性def隻是在通路一個字段,例如:

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

他們的理由是,如果需要,将來可以将公共場所轉換為财産。 當時對我來說有點奇怪。 從這些文章來看,似乎也沒有多少人同意。 您可能會說些什麼來嘗試改變事情?

編輯:我應該補充一點,此位置的所有代碼庫都是在同一時間編譯的,是以他們可能認為更改類的公共接口(通過将公共字段更改為屬性)不是問題。

#2樓

使用屬性,可以在更改屬性值(也稱為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);
   }
 }
}
           

#3樓

差異-用途(何時以及為什麼)

字段是直接在類或結構中聲明的變量。 一個類或結構可以具有執行個體字段或靜态字段,或兩者都有。 通常,應僅将字段用于具有私有或受保護的可通路性的變量 。 您的類公開給用戶端代碼的資料應通過方法,屬性和索引器提供。 通過使用這些結構間接通路内部字段,可以防止輸入值無效。

屬性是提供靈活機制以讀取,寫入或計算私有字段的值的成員。 可以将屬性當作公共資料成員使用,但實際上它們是稱為accessors的特殊方法。 這使資料易于通路,并且仍然有助于提高方法的安全性和靈活性 。 屬性使類可以公開擷取和設定值的公共方式,同時隐藏實作或驗證代碼。 擷取屬性通路器用于傳回屬性值,而設定通路器用于配置設定新值。

#4樓

從技術上講,我認為沒有什麼差別,因為屬性隻是使用者建立或自動由編譯器建立的字段的包裝器。屬性的目的是強制封裝并提供類似于方法的輕量級功能。 将字段聲明為公共字段是一種不好的做法,但是沒有任何問題。

#5樓

IMO,Properties隻是我們之前使用的“ SetXXX()”“ GetXXX()”函數/方法/接口對,但它們更加簡潔優雅。

#6樓

當您希望其他類的類對象可以通路自己的私有變量(字段)時,需要為這些變量建立屬性。

例如,如果我有一個名為“ 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";
    }
}
           

#7樓

由于其中許多人已經解釋了

Properties

Field

技術利弊,是以現在該開始研究實時示例了。

1.屬性允許您設定隻讀通路級别

考慮

dataTable.Rows.Count

dataTable.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中的屬性

您可能已經在Visual Studio中使用

Button

。 它的屬性顯示在

PropertyGrid

例如

Text

Name

等。當我們拖放按鈕時,當我們單擊屬性時,它将自動找到

Button

類并過濾

Properties

并在

PropertyGrid

顯示該

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

,将顯示屬性

Name

Text

,但不顯示

SomeProperty

。 為什麼??? 因為Properties可以接受Attributes 。 如果

[Browsable(false)]

為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.在綁定源中隻能使用屬性

綁定源可以幫助我們減少代碼行數。

BindingSource

不接受

Fields

。 我們應該為此使用

Properties

5.調試模式

考慮我們正在使用

Field

來儲存值。 在某些時候,我們需要調試并檢查該字段的值在哪裡變為空。 如果代碼行數超過1000,将很難做到。在這種情況下,我們可以使用

Property

并可以在

Property

内設定調試模式。

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

#8樓

傳統上,私有字段是通過getter和setter方法設定的。 為了減少代碼,您可以使用屬性來設定字段。

#9樓

當您有一個“汽車”課程時。 屬性是顔色,形狀。

其中as字段是在類範圍内定義的變量。

#10樓

屬性公開字段。 字段(幾乎總是)應該對類保持私有,并可以通過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指出,當通路屬性時,您還可以執行其他邏輯,例如驗證,這是另一個有用的功能。

#11樓

面向對象的程式設計原理說,一個類的内部工作應該對外界隐藏。 如果公開一個字段,則本質上就是公開該類的内部實作。 是以,我們使用屬性(或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
}
           

#12樓

如果要使用線程原語,則必須使用字段。 屬性會破壞您的線程代碼。 除此之外,科裡所說的是正确的。

#13樓

屬性封裝字段,進而使您可以對要設定或檢索的值執行其他處理。 如果您将不對字段值進行任何預處理或後處理,則使用屬性通常是過高的。

#14樓

屬性的主要優點是允許您更改通路對象上資料的方式而不會破壞其公共接口。 例如,如果您需要添加額外的驗證,或者将存儲的字段更改為計算字段,那麼如果您最初将該字段公開為屬性,則可以輕松地做到這一點。 如果您隻是直接公開一個字段,則必須更改類的公共接口以添加新功能。 這種更改将破壞現有的用戶端,要求他們重新編譯後才能使用新版本的代碼。

如果編寫用于廣泛使用的類庫(例如數百萬人使用的.NET Framework),則可能會出現問題。 但是,如果您要在一個小的代碼庫(例如<= 5萬行)内編寫一個内部使用的類,那實際上就沒什麼大不了的,因為沒有人會受到您的更改的不利影響。 在那種情況下,這實際上取決于個人喜好。

#15樓

同樣,屬性允許您在設定值時使用邏輯。

是以,可以說您隻想将一個值設定為整數字段(如果該值大于x),否則抛出異常。

真正有用的功能。

#16樓

在背景将屬性編譯為方法。 是以,将

Name

屬性編譯為

get_Name()

set_Name(string value)

。 如果您研究編譯的代碼,則可以看到此資訊。 是以,使用它們時(非常)很小的性能開銷。 通常,如果将字段暴露在外部,則将始終使用屬性;如果需要驗證值,則經常在内部使用它。

#17樓

屬性支援非對稱通路,即,您可以具有getter和setter或僅是兩者之一。 類似地,屬性支援對getter / setter的個别可通路性。 字段始終是對稱的,即,您始終可以同時擷取和設定值。 唯一的例外是隻讀字段,在初始化後顯然無法設定該字段。

屬性可能會運作很長時間,有副作用,甚至可能引發異常。 字段速度快,沒​​有副作用,并且永遠不會抛出異常。 由于副作用,屬性可能會為每個調用傳回不同的值(如DateTime.Now可能就是這樣,即DateTime.Now并不總是等于DateTime.Now)。 字段始終傳回相同的值。

字段可以用于out / ref參數,而屬性可以不使用。 屬性支援其他邏輯–可以用于實作延遲加載等。

屬性通過封裝擷取/設定值的含義來支援抽象級别。

在大多數情況下都使用屬性,但要避免産生副作用。

#18樓

一個重要的差別是接口可以具有屬性,但不能具有字段。 對我而言,這強調了應使用屬性來定義類的公共接口,而字段則應在類的私有内部工作中使用。 通常,我很少建立公共字段,并且類似地,我很少建立非公共屬性。

#19樓

我将為您提供一些使用屬性的示例,這些屬性可能會導緻齒輪旋轉:

  • 延遲初始化 :如果您擁有一個對象的屬性,該屬性的加載成本很高,但是在正常的代碼運作中卻沒有得到太多通路,則可以通過該屬性延遲其加載。 這樣,它就坐在那裡,但是當另一個子產品第一次嘗試調用該屬性時,它會檢查基礎字段是否為空-如果是,它将繼續進行加載并将其加載,而調用子產品則未知。 這樣可以大大加快對象的初始化速度。
  • 髒跟蹤:我實際上是從我在StackOverflow上自己的問題中學到的。 當我有很多對象的值在運作中可能已更改時,我可以使用該屬性來跟蹤是否需要将它們儲存回資料庫。 如果對象的單個屬性沒有更改,則IsDirty标志不會被觸發,是以,在确定需要傳回資料庫的内容時,儲存功能将跳過該标志。

#20樓

MSDN上的這個頁面有一個比較和提示,在以下情況下使用哪一個:

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

#21樓

字段是普通成員變量或類的成員執行個體。 屬性是擷取和設定其值的抽象 。 屬性也稱為通路器,因為如果您将類中的字段公開為私有,則它們提供了一種更改和檢索字段的方法。 通常,應将成員變量聲明為私有,然後為其聲明或定義屬性。

class SomeClass
  {
     int numbera; //Field

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

  }
           

#22樓

這裡的第二個問題,“當應在現場使用,而不是财産?”,僅在簡要介紹了這對方的回答還挺這一個了 ,但沒有真正的細節。

總的來說,所有其他答案都是關于良好設計的問題:相對于暴露字段,更喜歡暴露屬性。 雖然你可能不會經常發現自己說“哇,想象有多少糟糕的事情是,如果我做了,而不是财産這個領域”,這是這麼多難得想到的情況下,你會說:“哇,感謝上帝,我在這裡使用的是田地,而不是财産。”

但是,字段具有優于屬性的一個優點,那就是它們可以用作“ ref” /“ out”參數。 假設您有一個具有以下簽名的方法:

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

并假設您想使用該方法來轉換這樣建立的數組:

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

那将是相當不錯的! 除非您有其他證明的測量方法,否則沒有理由發臭。 但我相信,從技術上講,它并不能保證達到以下速度:

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參數可能是有益的,例如在調用Interlocked或Volatile系列方法時。 注意:如果這是您的新手,那麼Volatile基本上是一種實作

volatile

關鍵字提供的相同行為的方法。 是以,就像

volatile

一樣,它不能神奇地解決所有線程安全問題,就像它的名字暗示的那樣。

我絕對不希望我提倡您去“哦,我應該開始顯示字段而不是屬性”。 關鍵是,如果您需要在帶有“ ref”或“ out”參數的調用中定期使用這些成員,尤其是在那些可能是簡單值類型,不太可能不需要任何屬性的增值元素的調用上,可以争論。

#23樓

來自Wikipedia- 面向對象的程式設計

面向對象程式設計(OOP)是一種基于“對象”概念的程式設計範式,“對象”是包含資料的資料結構, 采用字段的形式 ,通常稱為屬性; 以及以程式形式的代碼,通常稱為方法 。 (強調)

屬性實際上是對象行為的一部分,但旨在使對象的使用者獲得使用對象資料的幻覺/抽象。

#24樓

屬性是一種特殊的類成員,在屬性中我們使用預定義的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;
    }
}
           

#25樓

我對字段的設計是,字段僅需要由其父級(即類)修改。 結果變量變為私有,然後能夠賦予讀取外部類/方法的權利,我僅使用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;
 }
}
           

#26樓

附加資訊:預設情況下,get和set通路器與屬性本身一樣可通路。 您可以通過對它們應用更嚴格的通路修飾符來單獨控制/限制通路者可通路性(對于get和set)。

例:

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

這裡get仍然是公開通路的(因為屬性是公共的),但是set受保護(更受限制的通路說明符)。

#27樓

屬性用于公開字段。 他們使用通路器(設定,擷取),通過它們可以讀取,寫入或操作私有字段的值。

屬性未命名存儲位置。 相反,它們具有讀取,寫入或計算其值的通路器。

使用屬性,我們可以對在字段上設定的資料類型設定驗證。

例如,我們有一個私有整數字段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; }
}
           

#28樓

考慮一下:您有一個房間,有一扇門可以進入這個房間。 如果要檢查誰進入房間并保護房間安全,則應使用屬性,否則它們将無門可乘,每個人都容易進入而無任何法規

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

現在,您檢查了此人,并知道他是否有惡魔

#29樓

在大多數情況下,它将是您通路的屬性名,而不是變量名( 字段 ),其原因是,在.NET和C#中,這被認為是一種很好的做法,尤其是保護類中的所有資料,無論是執行個體變量還是靜态變量(類變量),因為它都與類相關聯。

使用相應的屬性保護所有這些變量,這些屬性使您可以定義, 設定和擷取 通路器 ,并在處理這些資料時進行驗證等操作。

但是在其他情況下,例如Math類 (系統名稱空間),該類中内置了幾個靜态屬性。 其中之一是數學常數PI

例如。 數學PI

而且由于PI是一條定義明确的資料,是以我們不需要具有多個PI副本,是以它始終是相同的值。 是以,有時會使用靜态變量在類的對象之間共享資料,但是當變量隻需要一個資料副本時,它們也常用于常量資訊。

#30樓

字段是類中的變量。 字段是您可以通過使用通路修飾符封裝的資料。

屬性與字段類似,因為它們定義狀态和與對象關聯的資料。

與字段不同,屬性具有一種特殊的文法,該文法控制一個人如何讀取資料和寫入資料,這些稱為get和set運算符。 設定邏輯通常可用于進行驗證。

#31樓

基本差別和一般差別是:

領域

  • 始終授予擷取和設定通路權限
  • 不會引起副作用(抛出異常,調用方法,更改字段(除已設定的字段外)等)

性質

  • 并非總是同時獲得擷取和設定通路權限
  • 可引起副作用

#32樓

盡管字段和屬性看起來彼此相似,但是它們是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

關鍵字,但是通過這種方式,您必須僅在構造函數中初始化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. 檢查

    FullName

    YearOfBirth

    的無效值。
  2. Age

    不可寫。 從

    YearOfBirth

    和目前年份開始

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

是以,如您所見,編譯器仍然使用字段來存儲值-因為字段是将值存儲到對象中的唯一方法。

如您所見,盡管屬性和字段具有相似的用法文法,但它們是非常不同的概念。 即使您使用自動屬性或事件,隐藏字段也是由編譯器生成的,用于存儲實際資料。

如果您需要使外部使用者(您的類的使用者)可以通路字段值,請不要使用公共字段或受保護的字段。 字段始終應标記為私有。 屬性使您可以進行值檢查,格式設定,轉換等,并且通常可以使您的代碼更安全,更易讀且可擴充,以備将來修改之用。