Keri sisuni

Objektide võrdlemine

Objektide võrdlemine pole nii lihtne kui see algul tunduda võib. Objektide võrdlemiseks saame kasutada Equals() meetodi ja võrdusmärke (==). Equals() meetod on osades objekt-orienteeritud keeltes ainuõige meetod kahe objekti võrdlemiseks. C# võimaldab mõlemat võrdlust.

Lihttüüpide võrdlused on juba olemas

Lihttüüpide (int, double, bool, string jne) jaoks pakub C# juba keele tasemel korrektseid võrdlusi ja nende tüüpide võrdlemisel me midagi täiendavat tegema ei pea.

Alustame näitega, kus kasutame objektide võrdlemiseks Equals() meetodi. Näites võrdleme kahte Person-tüüpi objekti. Mõlemal isikul on sama eesnimi ja perekonnanimi.

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

var person1 = new Person { FirstName = "John", LastName = "Dow" };
var person2 = new Person { FirstName = "John", LastName = "Dow" };
var person3 = person1;

Console.WriteLine(person1.Equals(person2));
Console.WriteLine(person1.Equals(person3));
using System;

namespace OOP
{
    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            var person1 = new Person { FirstName = "John", LastName = "Dow" };
            var person2 = new Person { FirstName = "John", LastName = "Dow" };
            var person3 = person1;

            Console.WriteLine(person1.Equals(person2));
            Console.WriteLine(person1.Equals(person3));
        }
    }
}

Tulemuseks saame

False
True

Eeldasime, et esimese võrduse tulemus võiks olla True, kuid tuli hoopis False. Teine võrdus on True ja me saame aru miks see nii on. Aga miks esimene võrdus väära tulemuse andis?

Vaikimisi võrdlus

C# annab igale loodud objektile ajutise unikaalse tunnuse, mis on unikaalne ainult antud programmi käivituse raames. See võib järgmistel käivitustel olla sama, kuid see pole garanteeritud. Objekti unikaalse tunnuse saame küsida GetHashCode() meetodi abil.

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

var person1 = new Person { FirstName = "John", LastName = "Dow" };
var person2 = new Person { FirstName = "John", LastName = "Dow" };

Console.WriteLine(person1.GetHashCode());
Console.WriteLine(person2.GetHashCode());
using System;

namespace OOP
{
    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var person1 = new Person { FirstName = "John", LastName = "Dow" };
            var person2 = new Person { FirstName = "John", LastName = "Dow" };

            Console.WriteLine(person1.GetHashCode());
            Console.WriteLine(person2.GetHashCode());
        }
    }
}

Näeme, et räsikoodid, millel võrdlus põhineb, on erinevad:

58225482
54267293

See tähendab, et ilma täiendava juhendamiseta C# neid kahte objekti võrduma ei oska panna.

Jäta meelde!

GetHashCode() antud räsikoodid on mõeldud C# enda jaoks objektide tuvastamiseks ning igapäevases programmeerimises neid räsikoode ei kasutata.

Paneme objektid võrduma

Selleks, et kahte Person-tüüpi objekti võrrelda, peame Person klassis määrama võrduse jaoks omad tingimused. Teeme nii, et kui eesnimi ja perekonnanimi on isikutel samad, siis on tegemist sama isikuga. Et võrdus töötaks, peame me looma oma enda versiooni Equals() meetodist.

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public override bool Equals(object o)
    {
        // Võrdlemine nulliga annab tulemuseks false
        if(o == null)
        {
            return false;
        }

        // Kui võrreldav objekt ei ole tüübilt Person, 
        // siis on tulemuseks false
        var otherPerson = o as Person;
        if(otherPerson == null)
        {
            return false;
        }

        // Kui mõlemad objektid on Person-tüüpi, siis võrdumiseks peavad
        // nende ees- ja perekonnanimed võrduma
        return (FirstName == otherPerson.FirstName &&
                LastName == otherPerson.LastName);
    }
}

var person1 = new Person { FirstName = "John", LastName = "Dow" };
var person2 = new Person { FirstName = "John", LastName = "Dow" };

Console.WriteLine(person1.Equals(person2));
Console.WriteLine(person1==person2);
using System;

namespace OOP
{
    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public override bool Equals(object o)
        {
            // Võrdlemine nulliga annab tulemuseks false
            if (o == null)
            {
                return false;
            }

            // Kui võrreldav objekt ei ole tüübilt Person, 
            // siis on tulemuseks false
            var otherPerson = o as Person;
            if (otherPerson == null)
            {
                return false;
            }

            // Kui mõlemad objektid on Person-tüüpi, siis võrdumiseks peavad
            // nende ees- ja perekonnanimed võrduma
            return (FirstName == otherPerson.FirstName &&
                    LastName == otherPerson.LastName);
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            var person1 = new Person { FirstName = "John", LastName = "Dow" };
            var person2 = new Person { FirstName = "John", LastName = "Dow" };

            Console.WriteLine(person1.Equals(person2));
            Console.WriteLine(person1 == person2);
        }
    }
}

Kummalisel kombel töötab Equals() meetod korrektselt, kuid võrdlemine kahe võrdusmärgi abil korrektset tulemust ei anna.

Operator overload

C# ja mitmed teised objekt-orienteeritud keeled võimaldavad meil objektidega seotud tehteid ise defineerida. Selle metoodika nimi on operator overload. Antud juhul on meil vaja defineerida Person-klasside vaheline võrdumine ja mittevõrdumine.

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public override bool Equals(object o)
    {
        // Võrdlemine nulliga annab tulemuseks false
        if (o == null)
        {
            return false;
        }

        // Kui võrreldav objekt ei ole tüübilt Person, 
        // siis on tulemuseks false
        var otherPerson = o as Person;
        if (otherPerson == null)
        {
            return false;
        }

        // Kui mõlemad objektid on Person-tüüpi, siis võrdumiseks peavad
        // nende ees- ja perekonnanimed võrduma
        return (FirstName == otherPerson.FirstName &&
                LastName == otherPerson.LastName);
    }

    // Defineerime võrduse Person-tüüpi objektide vahel
    public static bool operator ==(Person v1, Person v2)
    {
        return v1.Equals(v2);
    }

    // Defineerime mittevõrduse Person-tüüpi objektide vahel
    public static bool operator !=(Person v1, Person v2)
    {
        return !v1.Equals(v2);
    }
}

var person1 = new Person { FirstName = "John", LastName = "Dow" };
var person2 = new Person { FirstName = "John", LastName = "Dow" };

Console.WriteLine(person1.Equals(person2));
Console.WriteLine(person1==person2);
using System;

namespace OOP
{
    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public override bool Equals(object o)
        {
            // Võrdlemine nulliga annab tulemuseks false
            if (o == null)
            {
                return false;
            }

            // Kui võrreldav objekt ei ole tüübilt Person, 
            // siis on tulemuseks false
            var otherPerson = o as Person;
            if (otherPerson == null)
            {
                return false;
            }

            // Kui mõlemad objektid on Person-tüüpi, siis võrdumiseks peavad
            // nende ees- ja perekonnanimed võrduma
            return (FirstName == otherPerson.FirstName &&
                    LastName == otherPerson.LastName);
        }

        // Defineerime võrduse Person-tüüpi objektide vahel
        public static bool operator ==(Person v1, Person v2)
        {
            return v1.Equals(v2);
        }

        // Defineerime mittevõrduse Person-tüüpi objektide vahel
        public static bool operator !=(Person v1, Person v2)
        {
            return !v1.Equals(v2);
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            var person1 = new Person { FirstName = "John", LastName = "Dow" };
            var person2 = new Person { FirstName = "John", LastName = "Dow" };

            Console.WriteLine(person1.Equals(person2));
            Console.WriteLine(person1 == person2);
        }
    }
}

Nüüd võrduvad person1 ja person2 muutujad nii Equals() meetodi kutsumisel kui kahe võrdusmärgi kasutamisel.

Räsikoodi loomine

Viimane asi, mille korda peame tegema, on räsikoodi genereerimine. Seda on vaja seepärast, et me muutsime objektide võrdlemist. Peatumata pikemalt räsikoodide loomise teemal on siin pakutud välja üks võimalik viis räsikoodi arvutamiseks.

public override int GetHashCode()
{
    int hash = 27;

    hash = (13 * hash) + (FirstName ?? "").GetHashCode();
    hash = (13 * hash) + (LastName ?? "").GetHashCode();

    return hash;
}

var person1 = new Person { FirstName = "John", LastName = "Dow" };
var person2 = new Person { FirstName = "John", LastName = "Dow" };

Console.WriteLine(person1.Equals(person2));
Console.WriteLine(person1.GetHashCode());
Console.WriteLine(person2.GetHashCode());
using System;

namespace OOP
{
    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public override bool Equals(object o)
        {
            // Võrdlemine nulliga annab tulemuseks false
            if (o == null)
            {
                return false;
            }

            // Kui võrreldav objekt ei ole tüübilt Person, 
            // siis on tulemuseks false
            var otherPerson = o as Person;
            if (otherPerson == null)
            {
                return false;
            }

            // Kui mõlemad objektid on Person-tüüpi, siis võrdumiseks peavad
            // nende ees- ja perekonnanimed võrduma
            return (FirstName == otherPerson.FirstName &&
                    LastName == otherPerson.LastName);
        }

        // Defineerime võrduse Person-tüüpi objektide vahel
        public static bool operator ==(Person v1, Person v2)
        {
            return v1.Equals(v2);
        }

        // Defineerime mittevõrduse Person-tüüpi objektide vahel
        public static bool operator !=(Person v1, Person v2)
        {
            return !v1.Equals(v2);
        }

        public override int GetHashCode()
        {
            int hash = 27;

            hash = (13 * hash) + (FirstName ?? "").GetHashCode();
            hash = (13 * hash) + (LastName ?? "").GetHashCode();

            return hash;
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            var person1 = new Person { FirstName = "John", LastName = "Dow" };
            var person2 = new Person { FirstName = "John", LastName = "Dow" };

            Console.WriteLine(person1.Equals(person2));
            Console.WriteLine(person1.GetHashCode());
            Console.WriteLine(person2.GetHashCode());
        }
    }
}

Väljund tuleb nüüd järgmine:

True
1382085349
1382085349

Näeme, et objektid võrduvad ning nende räsikoodid on samad.

Viited