Obiekty — tworzenie wystąpień typów

Definicja klasy lub struktury przypomina strategię określającą, co może zrobić typ. Obiekt jest blokiem pamięci, który program przydziela i konfiguruje zgodnie z strategią. Program może utworzyć wiele obiektów tej samej klasy. Obiekty można również nazywać instancjami. Można je przechowywać w zmiennej o określonej nazwie, tablicy lub kolekcji. Kod klienta używa tych zmiennych do wywoływania metod i uzyskiwania dostępu do właściwości publicznych obiektu. W języku obiektowym, takim jak C#, typowy program składa się z wielu obiektów współdziałających dynamicznie.

Uwaga

Typy statyczne zachowują się inaczej niż to, co opisano w tym artykule. Aby uzyskać więcej informacji, zobacz Klasy statyczne i składowe klas statycznych.

Instancje struktur a instancje klas

Ponieważ klasy są typami referencyjnymi, zmienna obiektu klasy przechowuje odwołanie do adresu obiektu na zarządzanym stercie. Jeśli przypiszesz drugą zmienną tego samego typu do pierwszej zmiennej, obie zmienne odwołują się do obiektu pod tym adresem. W tym artykule bardziej szczegółowo omówiono ten punkt.

Instancje klas tworzy się za pomocą operatora new. W poniższym przykładzie Person jest to typ i person1person2 są wystąpieniami lub obiektami tego typu.

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
        */
    }
}

Ponieważ struktury są typami wartości, zmienna obiektu struktury przechowuje kopię całego obiektu. Możesz również tworzyć wystąpienia struktur przy użyciu new operatora , ale nie musisz go używać, jak pokazano w poniższym przykładzie:

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
    */
}

Stos wątków przydziela pamięć dla elementów p1 i p2. Program odzyskuje tę pamięć wraz z typem lub metodą, w której ją deklarujesz. To zarządzanie pamięcią jest jednym z powodów, dla których struktury są kopiowane podczas przypisywania. Natomiast wspólne środowisko uruchomieniowe automatycznie odzyskuje pamięć (dokonuje jej odśmiecania) przydzieloną dla wystąpienia klasy, gdy wszystkie odwołania do obiektu wychodzą poza zakres. Nie można deterministycznie zniszczyć obiektu klasy, tak jak w języku C++. Aby uzyskać więcej informacji na temat odzyskiwania pamięci na platformie .NET, zobacz Odzyskiwanie pamięci.

Uwaga

Wspólne środowisko uruchomieniowe znacznie optymalizuje alokację i zwalnianie pamięci na zarządzanej stercie. W większości przypadków nie ma znaczącej różnicy w koszcie wydajnościowym przydzielania wystąpienia klasy na stercie w porównaniu z przydzielaniem wystąpienia struktury na stosie.

Tożsamość obiektu a równość wartości

Podczas porównywania dwóch obiektów równości najpierw zdecyduj, czy dwie zmienne reprezentują ten sam obiekt w pamięci, czy wartości jednego lub kilku pól są równoważne. Jeśli chcesz porównać wartości, rozważ, czy obiekty są wystąpieniami typów wartości (struktur) lub typów odwołań (klas, delegatów, tablic).

  • Użyj metody statycznej Object.ReferenceEquals , aby określić, czy dwa wystąpienia klasy odwołują się do tej samej lokalizacji w pamięci (co oznacza, że mają tę samą tożsamość). (System.Object jest niejawną klasą bazową dla wszystkich typów wartości i typów referencyjnych, w tym struktur zdefiniowanych przez użytkownika i klas).

  • Domyślnie metoda ValueType.Equals określa, czy pola instancji w dwóch instancjach struktury mają te same wartości. Ponieważ wszystkie struktury niejawnie dziedziczą z System.ValueType, metoda jest wywoływana bezpośrednio na obiekcie, jak pokazano w poniższym przykładzie.

    // 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.
    

    Domyślna System.ValueType implementacja Equals używa boksowania i refleksji w niektórych przypadkach. Aby uzyskać informacje na temat zapewniania efektywnego algorytmu równości specyficznego dla danego typu, zobacz Implementowanie równości samodzielnie, gdy typ nie może być rekordem. Rekordy to typy referencyjne, które używają semantyki wartości w celu zapewnienia równości.

  • Aby określić, czy wartości pól w dwóch wystąpieniach klasy są równe, możesz użyć Equals metody lub operatora ==. Jednak należy ich używać tylko wtedy, gdy klasa przesłoniła lub przeciążyła je, aby zapewnić niestandardową definicję tego, co oznacza "równość" dla obiektów tego typu. Klasa może również implementować IEquatable<T> interfejs lub IEqualityComparer<T> interfejs. Oba interfejsy udostępniają metody, które mogą służyć do testowania równości wartości. Podczas projektowania własnych klas, które przesłaniają Equals, pamiętaj, aby postępować zgodnie z wytycznymi określonymi w Samodzielne implementowanie równości, gdy typ nie może być rekordem i Object.Equals(Object).

Aby uzyskać więcej informacji, zobacz: