Objecten - exemplaren van typen maken

Een klasse- of structdefinitie lijkt op een blauwdruk die aangeeft wat het type kan doen. Een object is een geheugenblok dat het programma toewijst en configureert op basis van de blauwdruk. Een programma kan veel objecten van dezelfde klasse maken. U kunt objecten ook instanties noemen. U kunt deze opslaan in een benoemde variabele of in een matrix of verzameling. Clientcode gebruikt deze variabelen om de methoden aan te roepen en toegang te krijgen tot de openbare eigenschappen van het object. In een objectgeoriënteerde taal zoals C# bestaat een typisch programma uit meerdere objecten die dynamisch communiceren.

Opmerking

Statische typen gedragen zich anders dan wat in dit artikel wordt beschreven. Zie Statische klassen en statische klasseleden voor meer informatie.

Structinstanties tegenover klasse-instanties

Omdat klassen verwijzingstypen zijn, bevat een variabele van een klasseobject een verwijzing naar het adres van het object op de beheerde heap. Als u een tweede variabele van hetzelfde type toewijst aan de eerste variabele, verwijzen beide variabelen naar het object op dat adres. In dit artikel wordt dit punt later nader besproken.

U maakt exemplaren van klassen met behulp van de new operator. In het volgende voorbeeld Person is dit het type en person1person2 het zijn exemplaren of objecten van dat type.

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

Omdat structs waardetypen zijn, bevat een variabele van een struct-object een kopie van het hele object. U kunt ook exemplaren van structs maken met behulp van de new operator, maar u hoeft deze niet te gebruiken, zoals wordt weergegeven in het volgende voorbeeld:

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

De threadstack wijst geheugen toe aan zowel p1 als p2. Het programma maakt dat geheugen vrij, samen met het type of de methode waarin u het declareert. Dit geheugenbeheer is één reden waarom structs worden gekopieerd bij toewijzing. De common language runtime neemt daarentegen automatisch het geheugen weer in (garbage collection) dat het toewijst aan een exemplaar van een klasse wanneer alle verwijzingen naar het object niet langer binnen het bereik vallen. U kunt een klasseobject niet deterministisch vernietigen, zoals in C++. Zie Garbagecollection voor meer informatie over garbagecollection in .NET.

Opmerking

De Common Language Runtime optimaliseert de toewijzing en vrijgave van geheugen op de beheerde heap in hoge mate. In de meeste gevallen is er geen significant verschil in de prestatiekosten van het toewijzen van een klasse-exemplaar aan de heap versus het toewijzen van een struct-exemplaar op de stack.

Objectidentiteit versus waarde gelijkheid

Wanneer u twee objecten vergelijkt voor gelijkheid, moet u eerst beslissen of u wilt weten of de twee variabelen hetzelfde object in het geheugen vertegenwoordigen of of de waarden van een of meer van hun velden gelijkwaardig zijn. Als u waarden wilt vergelijken, kunt u overwegen of de objecten exemplaren van waardetypen (structs) of verwijzingstypen (klassen, gemachtigden, matrices) zijn.

  • Gebruik de statische Object.ReferenceEquals methode om te bepalen of twee klasse-exemplaren verwijzen naar dezelfde locatie in het geheugen (wat betekent dat ze dezelfde identiteit hebben). (System.Object is de impliciete basisklasse voor alle waardetypen en referentietypen, inclusief door de gebruiker gedefinieerde structs en klassen.)

  • De ValueType.Equals-methode bepaalt standaard of de instantievelden in twee struct-instanties dezelfde waarden hebben. Omdat alle structs impliciet overnemen van System.ValueType, roept u de methode rechtstreeks aan op uw object, zoals wordt weergegeven in het volgende voorbeeld:

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

    De standaard System.ValueType-implementatie van Equals gebruikt boksen en reflectie in sommige gevallen. Zie Gelijkheid zelf implementeren wanneer een type geen record kan zijn voor informatie over het bieden van een efficiënt gelijkheidsalgoritmen die specifiek zijn voor uw type. Records zijn referentietypen die gebruikmaken van waardesemantiek voor gelijkheid.

  • Als u wilt bepalen of de waarden van de velden in twee klasse-exemplaren gelijk zijn, kunt u mogelijk de Equals methode of de operator == gebruiken. Gebruik ze echter alleen als de klasse deze heeft overschreven of overbelast om een aangepaste definitie te geven van wat "gelijkheid" betekent voor objecten van dat type. De klasse kan ook de IEquatable<T> interface of de IEqualityComparer<T> interface implementeren. Beide interfaces bieden methoden die kunnen worden gebruikt om gelijkheid van waarden te testen. Wanneer u uw eigen klassen ontwerpt die Equals overschrijven, zorg er dan voor dat u de richtlijnen volgt die worden beschreven in Gelijkheid zelf implementeren wanneer een type geen record kan zijn en Object.Equals(Object).

Voor meer informatie, zie: