Entwickler-Ecke

Basistechnologien - Custom-Generic.List.Sort<>()


VampireSilence - So 26.06.11 19:17
Titel: Custom-Generic.List.Sort<>()
Also ich habe mir mit den Custom Generics eine eigene FileCollection erstellt und möchte die nun nach Datum sortieren. Die zugehörige Klasse sieht folgendermaßen aus:


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:
45:
46:
47:
48:
49:
50:
51:
52:
53:
public class FileCollection<T> : IEnumerable<T>
    {
        private List<T> arFiles = new List<T>();

        public T Get(int pos)
        { return arFiles[pos]; }

        public void Add(T c)
        { arFiles.Add(c); }

        public void Sort()
        { arFiles.Sort(); }

        public void Sort(bool Descending)
        { arFiles.Sort(new FileCollectionItemComparer(Descending)); }

        public void Clear()
        { arFiles.Clear(); }

        public int Count
        { get { return arFiles.Count; } }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        { return arFiles.GetEnumerator(); }

        System.Collections.IEnumerator IEnumerable.GetEnumerator()
        { return arFiles.GetEnumerator(); }

        private class FileCollectionItemComparer : IComparer<T>
        {
            private bool _Descending = false;

            public FileCollectionItemComparer(bool Descending)
            {
                this._Descending = Descending;
            }

            public int Compare(FileCollectionItem x, FileCollectionItem y)
            {
                if (_Descending)
                {
                    return (int)y.LastFileAccess.Subtract(x.LastFileAccess).TotalSeconds;
                }
                else
                {
                    return (int)x.LastFileAccess.Subtract(y.LastFileAccess).TotalSeconds;
                }
            }

            public int Compare(T x, T y)
            { return 0; }
        }
    }

Das Ganze habe ich initialisiert mit:

C#-Quelltext
1:
FileCollection<FileCollectionItem> _FileList = new FileCollection<FileCollectionItem>();                    

Und das FileCollectionItem habe ich folgendermaßen aufgebaut:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
    struct FileCollectionItem
    {
        public string Name, Fullpath, Extension;
        public DateTime LastFileAccess;

        public FileCollectionItem(string Path, DateTime LastFileAccess)
        {
            string[] parts = Path.Split(new string[] { "\\""." }, StringSplitOptions.RemoveEmptyEntries);

            this.Fullpath = Path;
            this.Extension = "." + parts[parts.Length - 1];
            this.Name = parts[parts.Length - 2] + this.Extension;
            this.LastFileAccess = LastFileAccess;
        }
    }

So. Und wenn ich jetzt aber _FileList.Sort(); oder _FileList.Sort(true); ausführe, passiert überhaupt garnichts. Wäre super, wenn ihr mir da ein bisschen auf die Sprünge helfen könntet.

mfg
- VampireSilence


Christian S. - So 26.06.11 19:35

Hallo!

Was mir erstmal auffällt: Bei Sort übergibst Du keinen Comparer.

Und dann stellt sich mir doch irgendwie die Frage, wozu Du eine eigene Klasse baust, wenn Du doch wieder nur die Methoden der List<T> benutzt.

Und was sind Custom Generics? Ich kenne nur "normale" Generics und was anderes benutzt Du auch nicht ;-)

Grüße
Christian

P.S.: Ich finde Comparison<T> ja weniger sperrig zu implementieren als IComparer<T>.


VampireSilence - So 26.06.11 19:46

Also ich habe 2 mal Sort(), einmal mit und einmal ohne Comparer. Im Programm benutze ich aber meistens dieser Version:

C#-Quelltext
1:
2:
public void Sort(bool Descending)
        { arFiles.Sort(new FileCollectionItemComparer(Descending)); }

Da brauche ich keinen Comparer übergeben, weil der in der Methode erst erstellt wird.

Die Methoden, der List<T> benutze ich, weils im Tutorial so da stand, ich keine Alternative kenne und bisher auch funktioniert hat. ^^

Naja und Custom ist es, weil ich keine bereits vorhandere nehme, wie StringCollection, sondern eine eigene erstelle. Custom halt. Wie Custom Contents bei PS3 Games. :P

mfg
- VampireSilence


Kha - So 26.06.11 20:13

Dass bei Sort() nichts passiert, kaufe ich dir nicht ab:
Zitat:
InvalidOperationException

The default comparer Comparer<T>.Default cannot find an implementation of the IComparable<T> generic interface or the IComparable interface for type T.


Bei deinem Comparer hast du dich mit den Generics ein wenig verzettelt. Sort ruft auf ihm IComparer<T>.Compare auf, was ja bei dir immer 0 zurückgibt ;) . Du hast da noch eine andere Methode drin, aber die hat durch ihre Parameter keine Beziehung zum Interface, wird also nie aufgerufen.
Die Lösung: Lass das T komplett weg. Wenn deine FileCollection FileCollectionItems enthalten soll, sag das auch so:

C#-Quelltext
1:
2:
3:
4:
public class FileCollection : IEnumerable<FileCollectionItem>
{
  ...
  private class FileCollectionItemComparer : IComparer<FileCollectionItem>


In allen anderen Punkten schließe ich mich Christian an :) . Und wo bei StringCollection Generics vorhanden sind, habe ich auch noch nicht herausbekommen :zwinker: .


VampireSilence - So 26.06.11 20:30

Ja, die Methode mit dem return 0; musste ich hinzufügen, weil der Compiler unbedingt eine Variante mit T haben wollte und T nunmal kein LastFileAccess besitzt. Da T einem FileCollectionItem entspricht, müsste doch aber auch die entsprechende Methode gewählt werden oder nicht ? Ich kann new string() doch schließlich auch einmal mit char* und einmal mit sbyte* aufrufen, woraufhin die jeweils zutreffende Variante gewählt wird ?

Naja ich hab T jetzt jedenfalls überall durch FileCollectionItem ersetzt und nun steht in der Zeile:

C#-Quelltext
1:
return (int)y.LastFileAccess.Subtract(x.LastFileAccess).TotalSeconds; //descending sort                    

hier folgendes:

C#-Quelltext
1:
"FileCollectionItem" enthält keine Definition für "LastFileAccess", und es konnte keine Erweiterungsmethode "LastFileAccess" gefunden werden, die ein erstes Argument vom Typ "FileCollectionItem" akzeptiert.                    


Und das obwohl nur ein paar Zeilen darunter LastFileAccess definitiv als public Datetime definiert ist UND obwohl es vorher mit T nie Probleme an dieser Stelle gab. Das verwirrt mich. -.-

Der Vollständigkeithalber hier nochmal der komplette Code:

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:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
public class FileCollection<FileCollectionItem> : IEnumerable<FileCollectionItem>
    {
        private List<FileCollectionItem> arFiles = new List<FileCollectionItem>();

        public FileCollectionItem Get(int pos)
        { return arFiles[pos]; }

        public void Add(FileCollectionItem c)
        { arFiles.Add(c); }

        public void Sort()
        { arFiles.Sort(); }

        public void Sort(bool Descending)
        { arFiles.Sort(new FileCollectionItemComparer(Descending)); }

        public void Clear()
        { arFiles.Clear(); }

        public int Count
        { get { return arFiles.Count; } }

        IEnumerator<FileCollectionItem> IEnumerable<FileCollectionItem>.GetEnumerator()
        { return arFiles.GetEnumerator(); }

        System.Collections.IEnumerator IEnumerable.GetEnumerator()
        { return arFiles.GetEnumerator(); }

        private class FileCollectionItemComparer : IComparer<FileCollectionItem>
        {
            private bool _Descending = false;
            
            public FileCollectionItemComparer(bool Descending)
            {
                this._Descending = Descending;
            }

            public int Compare(FileCollectionItem x, FileCollectionItem y)
            {
                if (_Descending)
                {
                    return (int)y.LastFileAccess.Subtract(x.LastFileAccess).TotalSeconds; //descending sort
                }
                else
                {
                    return (int)x.LastFileAccess.Subtract(y.LastFileAccess).TotalSeconds; //ascending sort
                }
            }

            public int Compare(T x, T y)
            { return 0; }
        }

    }

    struct FileCollectionItem
    {
        public string Name, Fullpath, Extension;
        public DateTime LastFileAccess;

        public FileCollectionItem(string Path, DateTime LastFileAccess)
        {
            string[] parts = Path.Split(new string[] { "\\""." }, StringSplitOptions.RemoveEmptyEntries);

            this.Fullpath = Path;
            this.Extension = "." + parts[parts.Length - 1];
            this.Name = parts[parts.Length - 2] + this.Extension;
            this.LastFileAccess = LastFileAccess;
        }
    }


mfg
- VampireSilence


Kha - So 26.06.11 20:37

Schau dir meinen Code bitte noch einmal an... du hast gerade einen Typparameter namens FileCollectionItem eingeführt :mrgreen: .

user profile iconVampireSilence hat folgendes geschrieben Zum zitierten Posting springen:
Da T einem FileCollectionItem entspricht
Das tut es, wenn du IComparer<FileCollectionItem> schreibst, aber ansonsten ist T ungebunden.


VampireSilence - Mo 27.06.11 00:38

Ähm, ja.. merke: Suchen&Alles ersetzen ist nicht immer dein Freund. ^^

Habs entfernt und nun gehts. Aber warum der IComparer im Einzelfall nicht die richtige Compare()-Variante wählt, ist mir immernoch schleierhaft.

Aber Danke für die Hilfe! :)

mfg
- VampireSilence