[c#] Qual è la differenza tra un campo e una proprietà?


Answers

I principi di programmazione orientati agli oggetti dicono che i meccanismi interni di una classe dovrebbero essere nascosti al mondo esterno. Se esponi un campo, in sostanza stai esponendo l'implementazione interna della classe. Quindi avvolgiamo campi con Proprietà (o metodi nel caso di Java) per darci la possibilità di cambiare l'implementazione senza infrangere il codice a seconda di noi. Vedere come possiamo inserire la logica nella proprietà ci consente anche di eseguire la logica di convalida ecc. Se ne abbiamo bisogno. C # 3 ha la nozione, forse confusa, di autoproprietà. Questo ci permette di definire semplicemente la proprietà e il compilatore C # 3 genererà il campo privato per noi.

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

In C #, cosa rende un campo diverso da una proprietà e quando dovrebbe essere usato un campo al posto di una proprietà?




quando hai una classe che è "Car". Le proprietà sono colore, forma ..

Dove i campi sono variabili definite nell'ambito di una classe.




La seconda domanda qui, "quando un campo dovrebbe essere usato al posto di una proprietà?", Viene solo brevemente accennata in questa altra risposta e anche questa , ma non molto, in dettaglio.

In generale, tutte le altre risposte sono chiare sul buon design: preferiscono esporre le proprietà oltre i campi di esposizione. Mentre probabilmente non ti troverai regolarmente a dire "wow, immagina quante cose peggiori sarebbero se avessi fatto di questo un campo invece di una proprietà", è molto più raro pensare a una situazione in cui potresti dire "wow, grazie a Dio ho usato un campo qui invece di una proprietà ".

Ma c'è un vantaggio che i campi hanno sulle proprietà, e questa è la loro capacità di essere usati come parametri "ref" / "out". Supponiamo di avere un metodo con la seguente firma:

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

e supponiamo che tu voglia usare quel metodo per trasformare un array creato in questo modo:

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

Ecco, penso che il modo più veloce per farlo, poiché X e Y sono proprietà:

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

E sarà abbastanza buono! A meno che tu non abbia misurazioni che dimostrano il contrario, non c'è motivo di gettare una puzza. Ma credo che non sia tecnicamente garantito essere così veloce come questo:

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

Effettuando alcune measurements solo, la versione con i campi richiede circa il 61% del tempo come versione con proprietà (.NET 4.6, Windows 7, x64, modalità di rilascio, nessun debugger collegato). Quanto più costoso diventa il metodo TransformPoint , tanto meno pronunciato diventa la differenza. Per ripetere tu stesso, corri con la prima riga commentata e con essa non commentata.

Anche se non ci sono benefici per le prestazioni di cui sopra, ci sono altri luoghi in cui la possibilità di utilizzare i parametri ref e out potrebbe essere utile, come quando si chiama la famiglia di metodi Interlocked o Volatile . Nota: nel caso in cui questo sia nuovo per te, Volatile è fondamentalmente un modo per ottenere lo stesso comportamento fornito dalla parola chiave volatile . Come tale, come volatile , non risolve magicamente tutti i problemi legati alla sicurezza del thread, come suggerisce il nome stesso.

Sicuramente non voglio sembrare come se stessi sostenendo che tu vai "oh, dovrei iniziare a esporre campi invece di proprietà". Il punto è che se hai bisogno di usare regolarmente questi membri in chiamate che prendono parametri "ref" o "out", specialmente su qualcosa che potrebbe essere un semplice tipo di valore che è improbabile che abbia mai bisogno di uno qualsiasi degli elementi a valore aggiunto delle proprietà, un argomento può essere fatto.







Le proprietà incapsulano i campi, consentendo così di eseguire un'ulteriore elaborazione sul valore da impostare o recuperare. Generalmente è eccessivo usare le proprietà se non si sta facendo alcun pre o post-elaborazione sul valore del campo.




Le proprietà sono un tipo speciale di membro della classe. Nelle proprietà utilizziamo un metodo Set o Get predefinito. Utilizzano gli accessor tramite i quali possiamo leggere, scrivere o modificare i valori dei campi privati.

Ad esempio, prendiamo una classe di nome Employee , con campi privati ​​per nome, età e Employee_Id. Non possiamo accedere a questi campi dall'esterno della classe, ma possiamo accedere a questi campi privati ​​attraverso le proprietà.

Perché usiamo le proprietà?

Rendere pubblico il campo classe ed esporlo è rischioso, in quanto non si avrà il controllo di ciò che viene assegnato e restituito.

Per comprenderlo chiaramente con un esempio, prendi una classe studentesca che ha ID, passmark, nome. Ora in questo esempio qualche problema con il campo pubblico

  • L'ID non dovrebbe essere -ve.
  • Il nome non può essere impostato su null
  • Il contrassegno deve essere letto solo
  • Se manca il nome dello studente, nessun nome deve essere restituito.

Per rimuovere questo problema Usiamo il metodo Get e 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
    }
}

Ora prendiamo un esempio di metodo get e 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;
    }
}



Le proprietà hanno il vantaggio principale di consentire di modificare il modo in cui si accede ai dati su un oggetto senza rompere la sua interfaccia pubblica. Ad esempio, se è necessario aggiungere una convalida aggiuntiva o modificare un campo memorizzato in un calcolo, è possibile farlo facilmente se inizialmente si espone il campo come proprietà. Se hai appena esposto un campo direttamente, dovresti modificare l'interfaccia pubblica della tua classe per aggiungere la nuova funzionalità. Quel cambiamento potrebbe rompere i client esistenti, richiedendo loro di essere ricompilati prima che potessero usare la nuova versione del tuo codice.

Se si scrive una libreria di classi progettata per un consumo ampio (come .NET Framework, che viene utilizzato da milioni di persone), ciò può rappresentare un problema. Tuttavia, se stai scrivendo una classe usata internamente in una piccola base di codice (diciamo <= 50 linee K), non è davvero un grosso problema, perché nessuno potrebbe essere influenzato negativamente dalle tue modifiche. In tal caso, si tratta solo di preferenze personali.




The vast majority of cases it's going to be a property name that you access as opposed to a variable name ( field ) The reason for that is it's considered good practice in .NET and in C# in particular to protect every piece of data within a class, whether it's an instance variable or a static variable (class variable) because it's associated with a class.

Protect all of those variables with corresponding properties which allow you to define, set and get accessors and do things like validation when you're manipulating those pieces of data.

But in other cases like Math class (System namespace), there are a couple of static properties that are built into the class. one of which is the math constant PI

per esempio. Math.PI

and because PI is a piece of data that is well-defined, we don't need to have multiple copies of PI, it always going to be the same value. So static variables are sometimes used to share data amongst object of a class, but the are also commonly used for constant information where you only need one copy of a piece of data.




Sullo sfondo una proprietà è compilata in metodi. Quindi una proprietà Name è compilata in get_Name() e set_Name(string value) . Puoi vedere questo se studi il codice compilato. Quindi c'è un sovraccarico di prestazioni (molto) piccolo quando li si usa. Normalmente si utilizzerà sempre una proprietà se si espone un campo all'esterno e lo si utilizzerà spesso internamente se è necessario eseguire la convalida del valore.




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



Ti darò un paio di esempi di utilizzo di proprietà che potrebbero far girare gli ingranaggi:

  • Inizializzazione pigra : se si dispone di una proprietà di un oggetto che è costoso da caricare, ma non si accede a tutto ciò nelle normali esecuzioni del codice, è possibile ritardarne il caricamento tramite la proprietà. In questo modo, è solo seduto lì, ma la prima volta che un altro modulo tenta di chiamare quella proprietà, controlla se il campo sottostante è nullo - se lo è, va avanti e lo carica, sconosciuto al modulo chiamante. Ciò può velocizzare notevolmente l'inizializzazione dell'oggetto.
  • Tracciamento sporco: che in realtà ho imparato dalla mia stessa domanda qui su . Quando ho molti oggetti che i valori potrebbero essere cambiati durante una corsa, posso usare la proprietà per tenere traccia se devono essere salvati nel database o meno. Se non è stata modificata una singola proprietà di un oggetto, il flag IsDirty non verrà attivato, e quindi la funzionalità di salvataggio salterà su di esso al momento di decidere cosa è necessario tornare al database.



IMO, le proprietà sono solo le coppie di funzioni / metodi / interfacce "SetXXX ()" "GetXXX ()" utilizzate in precedenza, ma sono più concise ed eleganti.




Dal momento che molti di loro hanno spiegato con pro e contro tecnici di Properties e Field , è il momento di entrare in esempi in tempo reale.

1. Proprietà consente di impostare il livello di accesso di sola lettura

Considera il caso di dataTable.Rows.Count e dataTable.Columns[i].Caption . Vengono dalla classe DataTable ed entrambi sono pubblici per noi. La differenza nel livello di accesso a loro è che non possiamo impostare valore su dataTable.Rows.Count ma possiamo leggere e scrivere su dataTable.Columns[i].Caption . È possibile attraverso il Field ? No!!! Questo può essere fatto solo con 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. Proprietà in PropertyGrid

Potresti aver lavorato con Button in Visual Studio. Le sue proprietà sono mostrate in PropertyGrid come Text , Name ecc. Quando trasciniamo un pulsante e quando clicchiamo sulle proprietà, troverà automaticamente la classe Button e filtri Properties e mostreremo che in PropertyGrid (dove PropertyGrid non mostrerà Field anche se sono pubblici).

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

In PropertyGrid , verranno mostrate le proprietà Name e Text , ma non SomeProperty . Perché??? Perché le proprietà possono accettare Attributes . Non viene visualizzato nel caso in cui [Browsable(false)] sia false.

3. Può eseguire istruzioni all'interno di Proprietà

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. Solo le proprietà possono essere utilizzate in Binding Source

Binding Source ci aiuta a ridurre il numero di righe di codice. Fields non sono accettati da BindingSource . Dovremmo usare le Properties per quello.

5. Modalità di debug

Considera che stiamo usando Field per mantenere un valore. A un certo punto dobbiamo eseguire il debug e verificare dove il valore sta diventando nullo per quel campo. Sarà difficile da fare dove il numero di righe di codice è più di 1000. In tali situazioni possiamo usare Property e possiamo impostare la modalità di debug all'interno di Property .

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



Se intendi utilizzare le primitive del thread, sei obbligato a utilizzare i campi. Le proprietà possono rompere il codice filettato. A parte ciò, ciò che ha detto è corretto.






Links