Oggetti: creare istanze di tipi
Una definizione di classe o struct è simile a un progetto iniziale in cui vengono specificate le funzionalità del tipo. Un oggetto è essenzialmente un blocco di memoria che è stato allocato e configurato in base al progetto iniziale. Un programma può creare molti oggetti della stessa classe. Gli oggetti, definiti anche istanze, possono essere archiviati in una variabile denominata o in una matrice o raccolta. Il codice client è il codice che usa queste variabili per chiamare i metodi e accedere alle proprietà pubbliche dell'oggetto. In un linguaggio orientato a oggetti come C#, il programma tipico è costituito da più oggetti che interagiscono dinamicamente.
Nota
I tipi statici si comportano in modo diverso da quanto descritto qui. Per altre informazioni, vedere Classi statiche e membri di classi statiche.
Differenze tra istanze di strutture e Istanze di classe
Poiché le classi sono tipi di riferimento, una variabile di un oggetto classe contiene un riferimento all'indirizzo dell'oggetto sull'heap gestito. Se alla prima variabile viene assegnata una seconda variabile dello stesso tipo, entrambe le variabili fanno riferimento all'oggetto in tale indirizzo. Questo punto viene illustrato in modo più dettagliato di seguito in questo articolo.
Le istanze delle classi vengono create usando l'operatore new
. Nell'esempio seguente Person
è il tipo e person1
e person2
sono le istanze o gli oggetti di tale tipo.
using System;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
// Other properties, methods, events...
}
class Program
{
static void Main()
{
Person person1 = new Person("Leopold", 6);
Console.WriteLine("person1 Name = {0} Age = {1}", person1.Name, person1.Age);
// Declare new person, assign person1 to it.
Person person2 = person1;
// Change the name of person2, and person1 also changes.
person2.Name = "Molly";
person2.Age = 16;
Console.WriteLine("person2 Name = {0} Age = {1}", person2.Name, person2.Age);
Console.WriteLine("person1 Name = {0} Age = {1}", person1.Name, person1.Age);
}
}
/*
Output:
person1 Name = Leopold Age = 6
person2 Name = Molly Age = 16
person1 Name = Molly Age = 16
*/
Poiché gli struct sono tipi di valore, una variabile di un oggetto struct contiene una copia dell'intero oggetto. È anche possibile creare istanze di struct usando l'operatore new
, tuttavia questa operazione non è necessaria, come illustrato nell'esempio seguente:
using System;
namespace Example
{
public struct Person
{
public string Name;
public int Age;
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
public class Application
{
static void Main()
{
// Create struct instance and initialize by using "new".
// Memory is allocated on thread stack.
Person p1 = new Person("Alex", 9);
Console.WriteLine("p1 Name = {0} Age = {1}", p1.Name, p1.Age);
// Create new struct object. Note that struct can be initialized
// without using "new".
Person p2 = p1;
// Assign values to p2 members.
p2.Name = "Spencer";
p2.Age = 7;
Console.WriteLine("p2 Name = {0} Age = {1}", p2.Name, p2.Age);
// p1 values remain unchanged because p2 is copy.
Console.WriteLine("p1 Name = {0} Age = {1}", p1.Name, p1.Age);
}
}
/*
Output:
p1 Name = Alex Age = 9
p2 Name = Spencer Age = 7
p1 Name = Alex Age = 9
*/
}
La memoria per p1
e p2
viene allocata nello stack di thread. Tale memoria viene recuperata insieme al tipo o al metodo in cui è dichiarata. Questo è il motivo per cui gli struct vengono copiati per assegnazione. Al contrario, la memoria allocata per l'istanza di una classe viene recuperata automaticamente (tramite Garbage Collection) da Common Language Runtime quando tutti i riferimenti all'oggetto sono usciti dall'ambito. Non è possibile eliminare in modo deterministico un oggetto classe come in C++. Per altre informazioni su Garbage Collection in .NET, vedere Garbage Collection.
Nota
L'allocazione e la deallocazione di memoria sull'heap gestito sono estremamente ottimizzate in Common Language Runtime. Nella maggior parte dei casi non esistono differenze significative in termini di impatto sulle prestazioni tra l'allocazione di un'istanza di classe sull'heap e l'allocazione di un'istanza di struttura sullo stack.
Differenze tra identità di oggetto e uguaglianza di valori
Quando si confrontano due oggetti per verificarne l'uguaglianza, è necessario innanzitutto distinguere se si vuole determinare se le due variabili rappresentano lo stesso oggetto in memoria oppure se i valori di uno o più campi sono equivalenti. Se si vuole confrontare valori, è necessario considerare se gli oggetti sono istanze di tipi di valore (struct) o di tipi di riferimento (classi, delegati, matrici).
Per determinare se due istanze di classe fanno riferimento alla stessa posizione in memoria (ovvero hanno la stessa identità), usare il metodo statico Object.Equals. System.Object è la classe di base implicita per tutti i tipi valore e i tipi riferimento, inclusi struct e classi definiti dall'utente.
Per determinare se i campi di istanza in due istanze di struct hanno gli stessi valori, usare il metodo ValueType.Equals. Poiché tutti gli struct ereditano implicitamente da System.ValueType, il metodo viene chiamato direttamente nell'oggetto, come illustrato nell'esempio seguente:
// Person is defined in the previous example. //public struct Person //{ // public string Name; // public int Age; // public Person(string name, int age) // { // Name = name; // Age = age; // } //} Person p1 = new Person("Wallace", 75); Person p2 = new Person("", 42); p2.Name = "Wallace"; p2.Age = 75; if (p2.Equals(p1)) Console.WriteLine("p2 and p1 have the same values."); // Output: p2 and p1 have the same values.
L'implementazione System.ValueType di
Equals
usa boxing e reflection in alcuni casi. Per informazioni su come fornire un algoritmo di uguaglianza efficiente specifico del tipo, vedere Come definire l'uguaglianza dei valori per un tipo. I record sono tipi riferimento che usano la semantica del valore per l'uguaglianza.Per determinare se i valori dei campi in due istanze di classe sono uguali, è possibile usare il metodo Equals o l'operatore ==. Tuttavia, usarli solo se la classe ha eseguito il loro override o overload per offrire una definizione personalizzata di cosa significa "uguaglianza" per gli oggetti di quel tipo. La classe può anche implementare l'interfaccia IEquatable<T> o IEqualityComparer<T>. Entrambe le interfacce offrono metodi che possono essere usati per verificare l'uguaglianza dei valori. Quando si progettano classi personalizzate che eseguono l'override di
Equals
, assicurarsi di seguire le linee guida indicate in Come definire l'uguaglianza dei valori per un tipo e Object.Equals(Object).
Sezioni correlate
Per altre informazioni: