Entwickler-Ecke

Basistechnologien - goto-Anweisung umgehen


lapadula - Mo 30.01.17 13:38
Titel: goto-Anweisung umgehen
Hallo, ich bin auf diese compare-Methode aus dem Internet gestoßen. Diese funktioniert wunderbar aber ich habe mir sagen lassen, dass die goto-Anweisung veraltet ist bzw. ein schlechter Programmier-Stil ist, da es unübersichtlich werden kann beim debuggen.

Ich habe das ganze versucht mit einer while-Schleife zu "übersetzen". Leider funktioniert dann das die Methode nicht richtig.

Kann mir einer sagen wie ich das sonst machen soll?


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
public int Compare(object a, object b)
        {
            int result;
            ListViewItem itemA = a as ListViewItem;
            ListViewItem itemB = b as ListViewItem;
            if (itemA == null && itemB == null)
                result = 0;
            else if (itemA == null)
                result = -1;
            else if (itemB == null)
                result = 1;
            if (itemA == itemB)
                result = 0;
            // datetime comparison
            DateTime x1, y1;
            // Parse the two objects passed as a parameter as a DateTime.
            if (!DateTime.TryParse(itemA.SubItems[Column].Text, out x1))
                x1 = DateTime.MinValue;
            if (!DateTime.TryParse(itemB.SubItems[Column].Text, out y1))
                y1 = DateTime.MinValue;

            result = DateTime.Compare(x1, y1);

            if (x1 != DateTime.MinValue && y1 != DateTime.MinValue)
                goto done;

            //alphabetic comparison
            result = String.Compare(itemA.SubItems[Column].Text, itemB.SubItems[Column].Text);

        done:
            // if sort order is descending.
            if (Order == SortOrder.Descending)
                // Invert the value returned by Compare.
                result *= -1;
            return result;
        }


Ralf Jansen - Mo 30.01.17 13:51

Da ist eine if Anweisung. In einem Fall soll der string returned werden mit dem alphabetic comparison Kommentar und im anderen Fall das was in done passiert. Ein if und 2.Möglichkeiten was muß man wohl tun?


Delete - Mo 30.01.17 13:56

- Nachträglich durch die Entwickler-Ecke gelöscht -


Ralf Jansen - Mo 30.01.17 14:00

Der ItemSorter für einen ListView muß aber IComparer auf ein ListViewItem implementieren. Ich vermute mal dazu gehört das Codebruchstück.


lapadula - Mo 30.01.17 14:55

@Ralf danke ich probier das mal aus.
@Frühlingsrolle ich werde den Sortierer im nächsten Projekt selber schreiben, bis dahin lasse ich es so. Es funktioniert bestens.

Da fehlt noch die Klasse:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
public class ItemComparer : IComparer
    {
        //column used for comparison
        public int Column;
        public int ColumnGetSet
        {
            get { return Column; }
            set { Column = value; }
        }
        //Order of sorting
        public SortOrder Order;
        public SortOrder OrderGetSet
        {
            get { return Order; }
            set { Order = value; }
        }
        public ItemComparer(int colIndex)
        {
            Column = colIndex;
            Order = SortOrder.None;
        }
        public int Compoare (object a, object b)
        {
         ...
        }


Und die Methode die ich aufrufe um zu sortieren:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
static public void Sort(ListView listView, int Column, SortOrder sortOrder)
        {
            if (listView != null)
            {
                ItemComparer sorter = listView.ListViewItemSorter as ItemComparer;
                if (sorter == null)
                {
                    sorter = new ItemComparer(Column);
                    sorter.Order = SortOrder.Ascending;
                    listView.ListViewItemSorter = sorter;
                }
                //Wenn eine Sortierrichtung übergeben wurde, dann wird entsprechend sortiert.
                //Grund: wird ein Eintrag in einer ListView editiert oder neu anlegelgt, dann ändert sich die Sortierrichtung nicht ständig,
                //dies soll nur dann geschehen, wenn auf die Header geklickt wird, siehe else
                if (sortOrder == SortOrder.Descending)
                {
                    sorter.Column = Column;
                    sorter.Order = SortOrder.Descending;
                }
                else if (sortOrder == SortOrder.Ascending)
                {
                    sorter.Column = Column;
                    sorter.Order = SortOrder.Ascending;
                }
                else
                {
                    //Wenn keine Sortierrichtung übergeben wurde (SortOrder.None), 
                    //dann wechelt sich asc und desc ab (wenn auf den selben ColumnHeader geklickt wurde)
                    if (Column == sorter.Column)
                    {
                        if (sorter.Order == SortOrder.Ascending)
                            sorter.Order = SortOrder.Descending;
                        else
                            sorter.Order = SortOrder.Ascending;
                    }
                    else
                    {
                        sorter.Column = Column;
                        sorter.Order = SortOrder.Ascending;
                    }
                }
                listView.Sort();
            }
        }


lapadula - Mo 30.01.17 15:07

Habe das ganze nun so versucht:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
if (x1 != DateTime.MinValue && y1 != DateTime.MinValue)
            {
                if (Order == SortOrder.Descending)
                    // Invert the value returned by Compare.
                    result *= -1;
                return result;
            }
            else
            {
                //alphabetic comparison
                result = String.Compare(itemA.SubItems[Column].Text, itemB.SubItems[Column].Text);
                return result;
            }


Leider sortiert er dann nicht mehr wie gewollt.


Th69 - Mo 30.01.17 15:51

In dem else-Fall soll doch weiterhin auf SortOrder.Descending geprüft werden, also:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
if (x1 == DateTime.MinValue || y1 == DateTime.MinValue) // Gegenteil von != ... && ... != 
    result = String.Compare(itemA.SubItems[Column].Text, itemB.SubItems[Column].Text); //alphabetic comparison

// if sort order is descending.
if (Order == SortOrder.Descending)    
    result *= -1// // Invert the value returned by Compare.

return result;


Ralf Jansen - Mo 30.01.17 16:06

Ich habe mich glaube ich missverständlich ausgedrückt ich wollte eigentlich das du schon viel früher abbiegst.
Das basteln mit DateTime.MinDate ist schon überflüssig.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
DateTime x1, y1;
if (DateTime.TryParse(itemA.SubItems[Column].Text, out x1) && DateTime.TryParse(itemB.SubItems[Column].Text, out y1))
    result = DateTime.Compare(x1, y1);
else
    result = String.Compare(itemA.SubItems[Column].Text, itemB.SubItems[Column].Text);

if (Order == SortOrder.Descending)
    result *= -1;
return result;


lapadula - Mo 30.01.17 17:00

@Th69 Danke so klappts.

Muss mir das nochmal in Ruhe anschauen

@Ralf Danke für den Ratschlag, ich werde die Methode dann doch nochmal umschreiben müssen.


hydemarie - Mo 30.01.17 21:09

Pro forma: Ein "goto" hat natürlich seine Berechtigung - intern macht der Compiler auch nix anderes. Man muss nicht unbedingt komplizierteren Code schreiben als nötig. Aber ab einer gewissen Komplexität wird es durchaus empfohlen, stattdessen Methoden zu verwenden.


lapadula - Mo 30.01.17 23:44

Ich hab irgendwo gelesen das das nutzen der goto Anweisung ein Kündigungsgrund sei.

Bei Erfahrenen Entwicklern nehme ich an


Christian S. - Mo 30.01.17 23:55

:rofl:


Ralf Jansen - Di 31.01.17 00:00

In den Best Practises steht sicher irgendwas von vermeiden von goto. Das ist auch fast immer richtig. Es gibt aber Fälle wo es sinnvoll sein kann goto zu benutzen .
Beispiele:
* Schwer lesbare mehrfache verschachtelter Schleifen. Wenn ich aus der innersten Schleife raus muss kann ein goto helfen das lesbar zu halten.
* cases in Switch Statements wo ein einfacher fallthrough nicht ausreicht und man zu einem case Label direkt springt
Natürlich macht man das aber erst (mit Bauchschmerzen) nachdem man genau die Alternativen durchdacht hat. Meist gibt es auch gut lesbare Alternativen, eigentlich sogar meist besser lesbare Alternativen, es gibt aber eben auch diese Fälle wo ein erzwingen einer Alternative es nur schlimmer macht anstatt besser.


hydemarie - Di 31.01.17 00:01

user profile iconlapadula hat folgendes geschrieben Zum zitierten Posting springen:
Ich hab irgendwo gelesen das das nutzen der goto Anweisung ein Kündigungsgrund sei.


Hängt von der Sprache ab. Schreib' mal ein Assemblerprogramm ohne Sprung. :twisted:


lapadula - Di 31.01.17 15:35

Mit Assembler kenne ich mich nicht aus aber Java hat goto auch irgendwann mal verbannt.

Die Sache mit dem Kündigungsgrund hat ein User auf stackoverflow erzählt, hatte den Eindruck er meint es ernst :D


hydemarie - Di 31.01.17 15:53

user profile iconlapadula hat folgendes geschrieben Zum zitierten Posting springen:
Java hat goto auch irgendwann mal verbannt.


Nun sollte man Java natürlich nicht unbedingt als lobendes Beispiel für irgendwas verwenden. Und tatsächlich kann Java stattdessen etwas viel Schlimmeres: break und continue mit Label [https://docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html]. :puke:


OlafSt - Mi 01.02.17 11:25

Ich programmiere nun schon ein paar Tage... In den 80er Jahren, auf den Homecomputern C64, Schneider CPC, TI-99 4/A usw. waren die Basic-Dialekte noch sehr rudimentär. Dort hatte man gar keine Wahl und mußte mit GOTO arbeiten. Als dann die ersten Dialekte mit GOSUB-Anweisungen kamen und auch verbesserte Schleifenkonstrukte mit DO..WHILE eingeführt wurden, verbesserte sich das ganze sehr.

Lasse ich diese Dinosaurier-Zeiten mal außen vor, habe ich in all den Jahrzehnten nur einen einzigen, wirklich notwendigen Fall für ein GOTO gesehen. Das war ein C-Programm, eine riesige, mehrfach verschachtelte Schleife, die sich über etliche hundert Zeilen zog und nur über ein GOTO sauber "mittendrin" verlassen werden konnte. Ansonsten habe ich nie wieder einen Anwendungsfall für GOTO gefunden.

Die Schleife zu entzerrren und aufs GOTO zu verzichten, hätte uns sicher drei Tage Arbeit gemacht. Wir haben uns lange im Team angesehen und überlegt - jeder hatte ein schmerzverzerrtes Gesicht, als wir uns fürs GOTO entschieden. So sehr sind wir indoktriniert, kein GOTO zu verwenden ;)