Laden...

Funktionsweise des IComparers

Erstellt von Luca_Aust vor 6 Jahren Letzter Beitrag vor 6 Jahren 3.119 Views
L
Luca_Aust Themenstarter:in
3 Beiträge seit 2017
vor 6 Jahren
Funktionsweise des IComparers

Moin,

Ich habe eine Object Klasse mit (string, int, int, float).

Diese habe ich mit

        

        private void GenerateObjects()                                           // Generate obects
        {
            bag = new BagObject[valueOfObjects];
            

            for (int counter = 0; counter <= valueOfObjects - 1; counter++)
            {
                string name = GetRandomName();
                int mass = GetRandomNumber();
                int price = GetRandomNumber();
                
                bag[counter] = new BagObject();

                bag[counter].SetBagObject(name, mass, price);

                bagList.Add(bag[counter]);

            }

            GetList();
        }


einer liste hinzu gefügt wobei die Länge der Liste von Benutzer bestimmt wird. (Der float wert berechnet sich aus mass und price)

Wobei mir gerade auffällt das wahrscheinlich unnötig ist die Obekte nochmal in einem Array zu speichern. Oder?

Nun möchte ich das ganze sortieren nach dem float wert.

Ich bekomme es zwar hin das mein IComparer Werte mit einander vergleicht aber auch nicht richtig.

Mein IComparer ist der den man überall findet da ich ihn einfach nicht so richtig verstehe:


 public static int CompareByEfficiency(BagObject objA, BagObject objB)           // Comparer for efficiency
        {
            BagObject o1 = objA;
            BagObject o2 = objB;

            // prüfen auf null-Übergabe
            if (o1.Efficiency == 0 && o2.Efficiency == 0) return 0;
            if (o1.Efficiency == 0) return 1;
            if (o2.Efficiency == 0) return -1;
            // Typüberprüfung
            if (o1.GetType() != o2.GetType())
                throw new ArgumentException("Ungültiger Vergleich");
            // Vergleich
            return o1.Efficiency.CompareTo(o2.Efficiency);
        }

jedoch sortiert er es so nicht richtig.

Meine Frage ist jetzt wie ich das jetzt richtig sortieren lasse das das Object mit dem niedrigsten float Wert oben in der Liste ist?
Ist der IComparer überhaupt das richtige dafür?

Da ich mir c# gerade selbst bei bringe kenne ich nicht viele Funktionen die die .NET Bib bietet.

Ich hoffe ich habe es gut formulieren können und habe es auch im richtigen Ort gepostet.

Ich danke schon mal für die Hilfe.

Gruß Luca

"TRÄUME SIND OFT DIE URSACHE MENSCHLICHEN ANTRIEBS."
(Troschka, Thorsten)

P
1.090 Beiträge seit 2011
vor 6 Jahren

Du willst doch die Werte des Objektes miteinander Vergleichen und nicht mit 0.

Könnte so passen, hab jetzt nicht auf die Reihenfolge von o1 und o1 geachtet. Und du solltest mal schauen, was passiert wenn ox == null.

    if (o1.Efficiency ==  o2.Efficiency ) return 0;
    if (o1.Efficiency < o2.Efficiency ) return 1;
    if (o2.Efficiency < o1.Efficiency) return -1;

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

3.003 Beiträge seit 2006
vor 6 Jahren

IComparer selbst sortiert nicht. Die Schnittstelle stellt lediglich die Funktion eines Vergleichs bereit, wie auch immer der aussieht. Und wer einen Vergleich braucht, um irgendwas zu tun, der kann eben eine IComparer-Implementierung verwenden. Ein Beispiel wäre ein Sortieralgorithmus: jeder Sortieralgorithmus muss in irgendeiner Weise durch die zu sortierende Liste gehen, und deren Elemente miteinander vergleichen sowie gegebenenfalls dann eben ihre Reihenfolge ändern.

Wenn du also einen Sortieralgorithmus hast, der so implementiert ist, dass man ihm einen IComparer geben kann, dann wird auch nach den Kriterien sortiert, die du im IComparer vorgegeben hast.


var sortedList = unsortedList.Sort(new MyComparer()); //sort ist der Algorithmus, dem man eine Vergleichsmethode geben kann,

Du solltest auch überlegen, ob in deinem Fall es nicht sinnvoller ist, wenn BagObject IComparable implementiert. (IComparable<T> vs. IComparer<T> ist dabei Thema vom Selbststudium 😉 ).

LaTino

Noch ein paar kleine Anmerkungen zum geposteten Code:

  • ValueOfObjects scheint eine Zahl zu sein. Wenn diese Zahl eine Anzahl ist, würde ich sie auch so nennen: ObjectCount zum Beispiel.
  • Anstatt Objekte einzeljn zu erzeugen, zu füllen und dann in eine Liste zu packen, mach doch alles in einem Aufwasch:

BagObject[] bagObjectList = Enumerable.Range(0, objectCount).Select(p => new BagObject 
{ 
    Name = GetRandomName,
    Mass = GetRandomNumber(),
    Price = GetRandomPrice()
}.ToArray();

//oder, wenn eine gute alte for-schleife angenehmer für die Augen ist:
private IEnumerable<BagObject> GenerateObjects()
{
     for(int i = 0; i < ObjectCount; i++)
         yield return new BagObject 
        { 
            Name = GetRandomName,
            Mass = GetRandomNumber(),
            Price = GetRandomPrice()
        };
}
//Anwendung: BagObject[] objectList = GenerateObjects().ToArray();

@Palin: dieser 0-Vergleich ist ein etwas verquerer Vergleich für nicht-initialisierte Werte. Würde ich ganz rauswerfen.

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

D
985 Beiträge seit 2014
vor 6 Jahren

Mal ne doofe Frage:

Wo seht ihr hier einen IComparer? Ich sehe nur etwas, was man einem Comparison<T> zuweisen könnte. 😉

und schreiben würde ich den auch eher so


public static int CompareByEfficiency(BagObject o1, BagObject o2)           // Comparer for efficiency
        {
            // prüfen auf null-Übergabe
            if (o1 == null && o2 == null ) return 0;
            if (o1 == null ) return 1;
            if (o2 == null) return -1;
            // Vergleich
            return o1.Efficiency.CompareTo(o2.Efficiency);
        }

3.003 Beiträge seit 2006
vor 6 Jahren

Mal ne doofe Frage:

Wo seht ihr hier einen IComparer? Ich sehe nur etwas, was man einem Comparison<T> zuweisen könnte. 😉

Ich hatte ihn so verstanden, dass er einen IComparer schreiben wollte, aber irgendwie durcheinandergekommen und beim Comparison<T>-Delegaten hängengeblieben ist. So richtig gibt das sein Posting aber nicht her, da haste Recht 😄.

LaTino

EDIT: bezeichnungen korrigiert

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

16.833 Beiträge seit 2008
vor 6 Jahren
  1. Bitte klar machen, worum es geht und wo das Problem ist.
  2. Einen ordentlichen Titel wählen. "Hilfe hilfe" ist keiner... 😉 Ich hab es korrigiert.
  3. Im richtigen Forum posten.

Wenn jemand Hilfe möchte, dann sind diese drei Punkte echt nicht zuviel verlangt.
[Hinweis] Wie poste ich richtig?
Danke.

L
Luca_Aust Themenstarter:in
3 Beiträge seit 2017
vor 6 Jahren

Hmm.. Also die Antworten die ich bis jetzt bekommen habe habe auch schon überall im Netz gefunden.
Und wenn das die einfachste Erklärung dafür ist Weiß ich auch nicht weiter.
Ich versuche es nochmal anders zu erklären.

Das hier ist meine BagObject class:



     public class BagObject
    {
        private string _name;
        private int _mass;
        private int _price;
        private float _efficiency;

        public string Name
        {
            get { return _name; }
            set
            {
                if (value.Length >= 3)
                {
                    _name = value;
                } else
                {
                    Console.WriteLine("Name need min. 3 Letters");
                }
            }
        }
        public int Mass
        {
            get { return _mass; }
            set
            {
                if (value > 0)
                {
                    _mass = value;
                } else
                {
                    Console.WriteLine("Weight can't be lower than 1");
                }
            }
        }
        public int Price
        {
            get { return _price; }
            set
            {
                if (_price < 1 && value > 0)
                {
                    _price = value;
                }else
                {
                    Console.WriteLine("Price can't be lower than 1 ");
                }
            }
        }
        public float Efficiency
        {
            get { return _efficiency; }
            set
            {
                if (_efficiency == 0)
                {
                    _efficiency = value;
                }
            }
        }
        
        public void SetBagObject (string name, int mass, int price)
        {
            Name = name;
            Mass = mass;
            Price = price;
            Efficiency = Mass / Price;
        }

Nun habe ich ja alles in bagList hinzugefügt.
Jetzt möchte ich, Efficiency von jedem Objekt in der Liste miteinenander vergleichen bwz sortieren das das Objekt mit dem kleinsten Efficiency Wert oben in der Liste ist bwz in einem Array.

Gibt es nicht eine möglichkeit zu sagen, das die liste nach einem bestimmten float der in dem aktuellen Objekt zu finden ist zu sortieren?

Ich bin gerade am verzweifeln weil es dafür bestimmt eine logische und gute Lösung gibt..

@LaTino

Kannst du mir einmal genau erklären was :



BagObject[] bagObjectList = Enumerable.Range(0, objectCount).Select(p => new BagObject.....


Ich kann damit ehrlich gesagt nicht so viel mit anfangen.

"TRÄUME SIND OFT DIE URSACHE MENSCHLICHEN ANTRIEBS."
(Troschka, Thorsten)

D
985 Beiträge seit 2014
vor 6 Jahren

Wenn du in die Dokumentation zu List<T> schaust, dann findest du auch List<T>.Sort (mehrere Überladungen) und eine davon nimmt auch ein Comparison<T> entgegen.


List<BagObject> bagList = ...
bagList.Sort( ???.CompareByEfficiency );

Das mit den ??? musst du selber herausfinden, denn du hast uns nicht mitgeteilt, in welcher Klasse du diese Methode CompareByEfficiency definiert hast.

5.658 Beiträge seit 2006
vor 6 Jahren

Hi Luca_Aust,

Jetzt möchte ich, Efficiency von jedem Objekt in der Liste miteinenander vergleichen bwz sortieren das das Objekt mit dem kleinsten Efficiency Wert oben in der Liste ist bwz in einem Array.

Das geht auch so:


List<BagObject> bags = ....
List<BagObject> sortedBags = bags.OrderBy(m => m.Efficiency).ToList();

Weeks of programming can save you hours of planning

16.833 Beiträge seit 2008
vor 6 Jahren

Tipp für die Zukunft:
in einem Forum fragen, wie man das Problem löst (hier Allgemein Sortierung) statt sein Problem (hier der falsche Umgang von IComparer) bzw. zumindest mal sagen, was man eigentlich vor hat.
Es ist ja auch nicht die richtige Vorgehensweise von Stuttgart 400 Km Richtung Hamburg zu fahren, um dann nach dem Weg nach München zu fragen 😉

Daher auch mein Hinweis:

  1. Bitte klar machen, worum es geht und wo das Problem ist.

PS:

     public string Name
        {
            get { return _name; }
            set
            {
                if (value.Length >= 3)
                {
                    _name = value;
                } else
                {
                    Console.WriteLine("Name need min. 3 Letters");
                }
            }
        }

führt sicherlich bald zum nächsten Problem.
Es gibt verschiedene Wege, Eingabevalidierungen umzusetzen - je nachdem, in welchem Kontext man arbeitet und was man genau erreichen will.
Der hier zählt zu keiner der möglichen, legitimen Wege.

5.658 Beiträge seit 2006
vor 6 Jahren

Das Gleiche betrifft auch die anderen Propertys. Efficiency kann bspw. keinen anderen Wert annehmen als 0. Da kann man sich auch die Sortierung sparen 😉

  
  
   
        public float Efficiency  
        {  
            get { return _efficiency; }  
            set  
            {  
                if (_efficiency == 0)  
                {  
                    _efficiency = value;  
                }  
            }  
        }  
  

Weeks of programming can save you hours of planning

16.833 Beiträge seit 2008
vor 6 Jahren

Efficiency muss 0 sein, um einen anderen Wert setzen zu können.
Ist Efficiency nicht 0, wird der Setter einfach ignoriert. Macht irgendwie keinen Sinn.

3.003 Beiträge seit 2006
vor 6 Jahren

@LaTino

Kannst du mir einmal genau erklären was :

  
  
BagObject[] bagObjectList = Enumerable.Range(0, objectCount).Select(p => new BagObject.....  
  
  

Ich kann damit ehrlich gesagt nicht so viel mit anfangen.

Deshalb habe ich darunter noch eine andere Schreibweise aufgeschrieben, die dasselbe macht. Wörtlich übersetzt macht der Code das hier:

  • für eine Menge von Zahlen (und zwar <objectCount> viele) (Enumerable.Range(0, objectCount)
  • erstelle pro Zahl eine Instanz der Klasse BagObject (Select p => new BagObject())
    (- sortiere nach der Eigenschaft "Efficiency") OrderBy(p => p.Efficiency)
  • und liefere die Ergebnismenge als Array (ToArray())

Alles zusammen:


BagObject[] orderedBagObjectList = Enumerable.Range(0, objectCount).Select(p => new BagObject
{
    Name = GetRandomName,
    Mass = GetRandomNumber(),
    Price = GetRandomPrice()
}.OrderBy(p => p.Efficiency).ToArray();

Ist nur eine Art, Dinge aufzuschreiben. Eine for-schleife wäre eine andere Art, diese hier nennt man Linq.

Davon abgesehen ist die Efficiency-Eigenschaft, nach der du sortieren möchtest, kaputt. Abt hat schon angedeutet, was da verkehrt ist. Ich glaube aber, der Sinn dahinter war, dass du eine Read-Only-Eigenschaft haben möchtest, die nur ein einziges Mal gesetzt wird und dann nie wieder verändert werden soll. Und ob sie gesetzt ist, erkennst du daran, dass sie nicht 0 ist.

Das ist
a) ein ganz furchtbarer Ansatz, weil eine Efficiency von 0 durchaus Sinn ergeben würde, deine Klasse das aber nicht abbilden kann und
b) das Rad neu (und schlechter) erfunden, denn so eine Mechanik existiert bereits. Eigentlich existieren sogar mehrere Möglichkeiten. Aber das sollte Thema eines anderen Threads sein.

LaTino
EDIT: Thread ergänzt

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

L
Luca_Aust Themenstarter:in
3 Beiträge seit 2017
vor 6 Jahren

Hi Luca_Aust,

Das geht auch so:

  
List<BagObject> bags = ....  
List<BagObject> sortedBags = bags.OrderBy(m => m.Efficiency).ToList();  
  

Ich habe es nun so gemacht und das Funktioniert auch so.

Davon abgesehen ist die Efficiency-Eigenschaft, nach der du sortieren möchtest, kaputt. Abt hat schon angedeutet, was da verkehrt ist.
Ich glaube aber, der Sinn dahinter war, dass du eine Read-Only-Eigenschaft haben möchtest, die nur ein einziges Mal gesetzt wird und dann nie wieder verändert werden soll.
Und ob sie gesetzt ist, erkennst du daran, dass sie nicht 0 ist.

Das ist
a) ein ganz furchtbarer Ansatz, weil eine Efficiency von 0 durchaus Sinn ergeben würde, deine Klasse das aber nicht abbilden kann und
b) das Rad neu (und schlechter) erfunden, denn so eine Mechanik existiert bereits. Eigentlich existieren sogar mehrere Möglichkeiten. Aber das sollte Thema eines anderen Threads sein.

Ja genau das war mein Gedanke. Danke für diesen hinweiß, ich werde mich dort einmal weiter einlesen.

Desweiteren werde ich beim nächsten mal versuchen richtig zu posten, einen passenden Titel auswählen und das Problem richtig zu beschreiben.
Da ich bis jetzt noch nie in einem Forum aktiv war ist das ganze auch noch neu für mich.

Danke an alle!

"TRÄUME SIND OFT DIE URSACHE MENSCHLICHEN ANTRIEBS."
(Troschka, Thorsten)