[C#] porovnání listů s objekty

C++, C#, Visual Basic, Delphi, Perl a ostatní

Moderátor: Moderátoři Živě.cz

Odeslat příspěvekod milanc 8. 12. 2016 02:50

Ahoj,

začínám s C# a potřebuji poradit. Mám nějaký List FuzzySet naplněný vlastními objekty - FuzzyElement.
Ve třídě Fuzzy element již mám implementaci IComparable pro porovnání dvou prvků.

Nyní se ale snažím porovnat dva Listy těchto objektů v rámci unit testu.

Pro rovnost to zkouším takto, ale jestli nemám chybu jinde, tak to nefunguje.
if (this.Count == other.Count && this.SequenceEqual(other))

Pak jsem zkoušel toto, ale také mi to vyhodnotí dvě stejné množiny jako nerovné.
if (this.Count == other.Count && this.SequenceEqual<FuzzyElement>(other))

Obecně mám trochu problém s tím, že nevím, jak ladit ty unit testy.
Jaký je přesně rozdíl mezi uvedenými příkazy, resp. jak to porovnává ten první? Ten druhý předpokládám využívá právě CompareTo?

Kód je zde: http://www.cizekmilan.cz/FuzzySet.zip
Děkuji za pomoc.
milanc
Junior
Uživatelský avatar

Odeslat příspěvekod Nargon 8. 12. 2016 06:45

IComparable se používá pro řazení, protože to poskytuje 3 stavy: -1,0,1 jednotlivé stavy odlišují zda jsou objekty stejné (0) a nebo zda je jeden větší/menší (1 a -1)
Ale pro obyčejné porovnání "stejnosti" (hrozný slovo co) se většinou používají metody GetHashCode: https://msdn.microsoft.com/en-us/librar ... hcode.aspx a Equals: https://msdn.microsoft.com/en-us/library/bsc2ak47.aspx takže ještě implementovat tyhle dvě metody a mělo by to začít porovnávat správně.

Ale jak to ladit, nebo jak je implementovaný ten jeden a ten druhý fakt nevím.
Desktop: Ryzen 7 1800X (3.95GHz, 1.35V), Asus Crosshair VI Hero, 16GB DDR4 Ram (3200MHz), 128GB SSD + 3TB HDD, Nvidia GTX 1080
Notebook: Asus UL50VT 15.6" (SU7300@1.7GHz, 4GB ram, 500GB HDD, Intel GMA 4500MHD + nVidia G210M, dlouha vydrz cca 7+ hod)
Nargon
Moderátor

Odeslat příspěvekod pucmeloudek 8. 12. 2016 08:32

krome toho, ze predseda ma pravdu (aneb zdurazneno: to, ze IComparable vraci 0, v zadnem pripade neznamena, ze jsou objekty stejne, jen to, ze jsou z hlediska razeni rovnocenne), tak rozdil mezi zapisy sequenceequal je pouze ten, ze v prvnim pripade si usetris trochu psani. mj. to poznas podle toho, ze novejsi verze visual studia ten zbytecny kus kodu udelaj takovej min vyraznej. uplatnuje se tam tzv. type inference, prekladac ti prislusny parametr generika sam domysli podle predaneho parametru.
pucmeloudek
Junior

Odeslat příspěvekod milanc 9. 12. 2016 03:53

Ahoj,

pokusil jsem se jako první upravit FuzzyElement. Přetížil jsem i == a !=, protože jestli správně chápu je to třeba, protože == používám v Equals. Nicméně mi to stále nefunguje. Pokud pomocí Equals porovnám dva prvky (různé objekty) se stejným Value i Grade, vrací mi chybně false.

Kód: Vybrat vše
using System;
using System.Globalization;

namespace Fuzzy
{
    [System.Diagnostics.DebuggerDisplay("\\{ Value = {X}, Grade = {Y} \\}")]
    public class FuzzyElement : IComparable<FuzzyElement>
    {
        public object Value { get; private set; }
        public double Grade { get; private set; }

        public FuzzyElement(object Value, double Grade)
        {
            this.Value = Value;
            this.Grade = Grade;
        }


        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }
            var other = obj as FuzzyElement;
            if ((object)other == null)
            {
                return false;
            }
            return this == other;
        }

        public bool Equals(FuzzyElement other)
        {
            if ((object)other == null)
            {
                return false;
            }
            return this == other;
        }

        public override int GetHashCode()
        {
            return this.Value.GetHashCode() ^ this.Grade.GetHashCode();
        }

        public static bool operator ==(FuzzyElement left, FuzzyElement right)
        {
            if (object.ReferenceEquals(left, right))
            {
                return true;
            }
            if ((object)left == null || (object)right == null)
            {
                return false;
            }
            return left.Value == right.Value && left.Grade == right.Grade;
        }

        public static bool operator !=(FuzzyElement left, FuzzyElement right)
        {
            return !(left == right);
        }

        public int CompareTo(FuzzyElement other)
        {
            if (!this.Value.Equals(other.Value))
                return -1;
            else
            {
                if (this.Grade < other.Grade)
                    return -1;
                else if (this.Grade > other.Grade)
                    return 1;
                else
                    return 0;
            }
        }

        public override string ToString()
        {
            // CultureInfo jen kuli tomu, že chci mít jako oddělovač desetinných míst "."
            return Grade.ToString(CultureInfo.CreateSpecificCulture("en-GB")) + "/" + Value.ToString();
        }

    }
}


Unit test
Kód: Vybrat vše
            FuzzyElement E4 = new FuzzyElement(5, 0.9);
            FuzzyElement E5 = new FuzzyElement(5, 0.9);

            // E4=E5, 5=5, 0.9=0.9 (stejné prvky, stejné stupně)
            Expected = true;
            Result = E4.Equals(E5);
             Assert.AreEqual(Expected, Result, String.Format("Porovnání-5 {0} = {1} -> {2}.",
                E4.ToString(), E5.ToString(), Result));


Když testuju E4 == E5, tak vrací false. Ale prostě nevidím v té metodě nic špatně.
milanc
Junior
Uživatelský avatar

Odeslat příspěvekod Nargon 9. 12. 2016 05:34

Doporučuji naučit se používat debugger a krokovat si jednotlivé části. Pak by jsi přišel na to samé co já.
False ti to vrací z důvodu podmínky u operátoru ==, tam je problém s tím porovnáním left.Value == right.Value.
Protože proměnná "Value" je typu object tak se zde neporovnává obsah ale pouze reference a ta každá ukazuje na jiný objekt takže vrátí false.
Kdyby jsi měl proměnou Value definovanou jako int tak ti to bude fungovat, protože u intů se operátorem == porovnává jejich hodnota, nikoli reference jako u object.
Desktop: Ryzen 7 1800X (3.95GHz, 1.35V), Asus Crosshair VI Hero, 16GB DDR4 Ram (3200MHz), 128GB SSD + 3TB HDD, Nvidia GTX 1080
Notebook: Asus UL50VT 15.6" (SU7300@1.7GHz, 4GB ram, 500GB HDD, Intel GMA 4500MHD + nVidia G210M, dlouha vydrz cca 7+ hod)
Nargon
Moderátor

Odeslat příspěvekod rudidlo 22. 12. 2016 16:19

Stačí implementovat IEquatable<T>, přepsat negenerickou metodu Equals(object o) a GetHashCode, jak píše Nargon a objekty pak porovnávat přes objectA.Equals(objectB)
Jsou dva druhy uživatelů počítačů. Ti první o svá data už přišli.
rudidlo
Junior
Uživatelský avatar

Odeslat příspěvekod satikcz 22. 12. 2016 16:44

Ještě dodám, že na testování kolekcí bys měl ideálně používat CollectionAssert:

https://msdn.microsoft.com/en-us/librar ... ssert.aspx
3x AOC AG271QG (2560x1440, IPS, 165Hz, GSync), ASUS Strix RTX3090 OC, Intel Core i7 8700K@5GHz+EKWB L360, ASUS Maximus X, 32GB G.SKILL TridentZ@3466 MHz, ADATA SX8200 2TB + 1TB + Crucial BX 500 GB + Kingston UV400 1TB, Seasonic P-860 Platinum, FD Define S
satikcz
Junior
Uživatelský avatar


Kdo je online

Uživatelé procházející toto fórum: Žádní registrovaní uživatelé a 0 návštevníků