Oggetti : creare istanze di tipi

Una definizione di classe o struct è simile a un progetto che specifica le operazioni che il tipo può eseguire. Un oggetto è un blocco di memoria che il programma alloca e configura in base al progetto. Un programma potrebbe creare molti oggetti della stessa classe. È anche possibile chiamare istanze di oggetti. È possibile archiviarli in una variabile denominata o in una matrice o in una raccolta. Il codice client usa queste variabili per chiamare i metodi e accedere alle proprietà pubbliche dell'oggetto. In un linguaggio orientato agli oggetti, ad esempio C#, un programma tipico è costituito da più oggetti che interagiscono dinamicamente.

Nota

I tipi statici si comportano in modo diverso rispetto a quanto descritto in questo articolo. Per altre informazioni, vedere Classi statiche e membri di classi statiche.

Istanze di struct vs. istanze di class

Poiché le classi sono tipi riferimento, una variabile di un oggetto classe contiene un riferimento all'indirizzo dell'oggetto nell'heap gestito. Se si assegna una seconda variabile dello stesso tipo alla prima variabile, entrambe le variabili fanno riferimento all'oggetto in tale indirizzo. Questo articolo illustra questo punto in modo più dettagliato più avanti.

È possibile creare istanze di classi usando l'operatorenew . Nell'esempio seguente, Person è il tipo e person1 sono person2 istanze, o oggetti, di tale tipo.

using System;

public class Person(string name, int age)
{
    public string Name { get; set; } = name;
    public int Age { get; set; } = age;
    // Other properties, methods, events...
}

class Program
{
    static void Main()
    {
        Person person1 = new("Leopold", 6);
        Console.WriteLine($"person1 Name = {person1.Name} Age = {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 = {person2.Name} Age = {person2.Age}");
        Console.WriteLine($"person1 Name = {person1.Name} Age = {person1.Age}");

        /*
            Output:
            person1 Name = Leopold Age = 6
            person2 Name = Molly Age = 16
            person1 Name = Molly Age = 16
        */
    }
}

Poiché gli struct sono tipi valore, una variabile di un oggetto struct contiene una copia dell'intero oggetto. È anche possibile creare istanze di struct usando l'operatore new , ma non è necessario usarlo, 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("Alex", 9);
            Console.WriteLine($"p1 Name = {p1.Name} Age = {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 = {p2.Name} Age = {p2.Age}");

            // p1 values remain unchanged because p2 is  copy.
            Console.WriteLine($"p1 Name = {p1.Name} Age = {p1.Age}");
        }
    }
    /*
        Output:
        p1 Name = Alex Age = 9
        p2 Name = Spencer Age = 7
        p1 Name = Alex Age = 9
    */
}

Lo stack del thread alloca memoria sia per p2 che per p1. Il programma recupera tale memoria insieme al tipo o al metodo in cui viene dichiarato. Questa gestione della memoria è un motivo per cui gli struct vengono copiati in fase di assegnazione. Al contrario, Common Language Runtime recupera automaticamente (Garbage Collect) la memoria allocata per un'istanza di classe quando tutti i riferimenti all'oggetto escono 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

Il Common Language Runtime ottimizza notevolmente l'allocazione e la deallocazione della memoria nell'heap gestito. Nella maggior parte dei casi, non esiste alcuna differenza significativa nel costo delle prestazioni dell'allocazione di un'istanza di classe nell'heap rispetto all'allocazione di un'istanza di struct nello stack.

Identità dell'oggetto e uguaglianza dei valori

Quando si confrontano due oggetti per verificarne l'uguaglianza, stabilire innanzitutto se le due variabili rappresentano lo stesso oggetto in memoria o se i valori di uno o più campi sono equivalenti. Se vuoi confrontare valori, considera se gli oggetti sono istanze di tipi di valore (struct) o di tipi di riferimento (classi, delegati, array).

  • Usare il metodo statico Object.ReferenceEquals per determinare se due istanze di classe fanno riferimento alla stessa posizione in memoria (ovvero hanno la stessa identità). (System.Object è la classe base implicita per tutti i tipi valore e i tipi riferimento, inclusi gli struct e le classi definiti dall'utente.

  • Per impostazione predefinita, il ValueType.Equals metodo determina se i campi dell'istanza in due istanze dello struct hanno gli stessi valori. Poiché tutti gli struct ereditano in modo implicito da System.ValueType, puoi chiamare il metodo direttamente sul proprio oggetto, come illustrato nell'esempio seguente.

    // Person is defined in the previous example.
    
    //public struct Person(string name, int age)
    //{
    //    public string Name { get; set; } = name;
    //    public int Age { get; set; } = age;
    //}
    
    Person p1 = new("Wallace", 75);
    Person p2 = new("", 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 predefinita System.ValueType di Equals utilizza il boxing e reflection in alcuni casi. Per informazioni su come fornire un algoritmo di uguaglianza efficiente specifico per il tipo, vedere Implementare manualmente l'uguaglianza quando un tipo non può essere un record. I record sono tipi di 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 Equals metodo o l'operatore == . Tuttavia, usarli solo se la classe ha eseguito l'override o li ha sottoposti a overload per fornire una definizione personalizzata del significato di "uguaglianza" per gli oggetti di tale tipo. La classe potrebbe anche implementare l'interfaccia IEquatable<T> o l'interfaccia IEqualityComparer<T> . Entrambe le interfacce forniscono metodi che possono essere usati per testare l'uguaglianza dei valori. Quando si progettano classi personalizzate che eseguono l'override di Equals, assicurarsi di seguire le linee guida riportate in Implementare autonomamente l'uguaglianza quando un tipo non può essere un record e Object.Equals(Object).

Per ulteriori informazioni consulta: