Bezugnehmend auf den Tipp Instanzen in einer ArrayList sortieren (VB.NET) möchten wir Ihnen heute eine verbesserte Version vorstellen, bei der die Basisrichtlinien für .NET Code berücksichtig werden: Es wird sehr empfohlen solche Klassen mit Properties zu versehen, und nicht mit Public Fields. Das erfordert, dass man nach Properties sucht. Aber hier sollten wir nicht ausser Acht lassen, dass Properties auch virtuell sein können (etwa ICustomTypeDescriptor ist implementiert). Das ist nicht völlig irrelevant, weil Windows Forms DataBinding überall mit Properties arbeitet, und man durchaus eigene Klassen mit ArrayLists und DataBinding verwenden könnte. Das verlangt aber, dass man nicht Reflection, sondern das Component Model PropertyDescriptor Mechanismus verwendet. Aber lassen wir das, und bleiben wir der Einfachkeit halber bei einfachen Properties und Reflection. Dann hätten wir immerhin eine saubere Klasse: Public Class Schiff Implements IComparable Private _name As String Private _heimathafen As String Private _baujahr As Integer Private _kaufpreis As Single Private _stapellauf As New Date Public Property Name() As String Get Return _name End Get Set(ByVal Value As String) _name = Value End Set End Property Public Property Heimathafen() As String Get Return _heimathafen End Get Set(ByVal Value As String) _heimathafen = Value End Set End Property Public Property Baujahr() As Integer Get Return _baujahr End Get Set(ByVal Value As Integer) _baujahr = Value End Set End Property Public Property Kaufpreis() As Single Get Return _kaufpreis End Get Set(ByVal Value As Single) _kaufpreis = Value End Set End Property Public Property Stapellauf() As DateTime Get Return _stapellauf End Get Set(ByVal Value As DateTime) _stapellauf = Value End Set End Property Public Shared SortierFeld As String = "Heimathafen" Public Shared Sortierung As SortOrder = SortOrder.Ascending Public Sub New(ByVal Name As String, ByVal Heimathafen As String, _ ByVal Baujahr As Integer, ByVal Kaufpreis As Single, ByVal Stapellauf As Date) Me.Name = Name Me.Heimathafen = Heimathafen Me.Baujahr = Baujahr Me.Kaufpreis = Kaufpreis Me.Stapellauf = Stapellauf End Sub Public Sub New() End Sub ' IComparable... End Class Die CompareTo() Routine ist sehr viel komplizierter als notwendig. Weil alles was man vernünftig vergleichen will (oder kann) IComparable unterstützt (Integer, String, Single usw) müssen wir die Parameter nur zu IComparable casten, und wenn das nicht klappt wird eine Exception ausgelöst, was sinnvoll ist weil wir die Werte dann eh nicht vergleichen können. So auszig Zeilen Code wird einfach Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo Dim s As Schiff s = CType(obj, Schiff) Dim f1 As Reflection.PropertyInfo f1 = Me.GetType.GetProperty(Me.SortierFeld) Dim f2 As Reflection.PropertyInfo f2 = s.GetType.GetProperty(Me.SortierFeld) Dim Wert1 As IComparable = CType(f1.GetValue(Me, Nothing), IComparable) Dim Wert2 As Object = f2.GetValue(obj, Nothing) Return Wert1.CompareTo(Wert2) End Function Aber so sind wir längst nicht fertig, zweimal wird ein PropertyInfo (ursprünglich FieldInfo) mittels Reflection geholt (eine teure Operation) obwohl wir wissen, dass wir es in beiden Fällen mit einem Objekt vom Typ Schiff zu tun haben. Wenn wir diese Schwäche auch ausbügeln haben wir: Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo Dim s As Schiff s = CType(obj, Schiff) Dim f1 As Reflection.PropertyInfo f1 = Me.GetType.GetProperty(Me.SortierFeld) Dim Wert1 As IComparable = CType(f1.GetValue(Me, Nothing), IComparable) Dim Wert2 As Object = f1.GetValue(obj, Nothing) Return Wert1.CompareTo(Wert2) End Function Allerdings, damit sind wir mit der Kritik auch längst nicht fertig. Reflection bei jedem Vergleich zwischen zwei Elementen des Arrays zu verwenden ist extrem ineffizient, und es ist sehr ungeschickt Parameter an CompareTo() mittels statischen (Shared) Variablen zu übergeben, und nicht nur ungeschickt sondern umständlich und fehleranfällig. Und reverse Sort zu implementieren indem wir das ganze Array umschaufeln ist auch nicht gerade elegent. Wesentlich sinnvoller wäre es ein IComparer Objekt bei arraylist.Sort() zu übergeben, wo die notwendigen Parameter im Klassenobjekt kapsuliert sind. Wir sind dann gleich alle drei Schwächen los, weil wir Reflection nur im Klassenkonstruktor benutzen müssen, und eine Sortoption auch übergeben können. Dann hätten wir die Klasse: Public Class Schiff ' Fields, Properties, Constructors...(Shared Felder entfallen) Public Class Comparer Implements IComparer Private _sortProperty As Reflection.PropertyInfo Private _sortDescending As Boolean Public Sub New(ByVal sortFieldName As String, ByVal sortDescending As Boolean) _sortProperty = GetType(Schiff).GetProperty(sortFieldName) _sortDescending = sortDescending End Sub Public Function Compare(ByVal obj1 As Object, ByVal obj2 As Object) As Integer Implements IComparer.Compare Dim Wert1 As IComparable = CType(_sortProperty.GetValue(obj1, Nothing), IComparable) Dim Wert2 As Object = _sortProperty.GetValue(obj2, Nothing) Dim res As Integer = Wert1.CompareTo(Wert2) If _sortDescending Then Return -res Return res End Function End Class End Class Der Aufruf im Hauptcode sieht denn so aus: AL.Sort(New Schiff.Comparer("Baujahr", False)) AL.Reverse() entfällt ebenfalls. Ein etwas erweitertes Beispiel Public Class Form1 Inherits System.Windows.Forms.Form #Region " Windows Form Designer generated code " Public Class Schiff Private _name As String Private _heimathafen As String Private _baujahr As Integer Private _kaufpreis As Single Private _stapellauf As New Date Public Property Name() As String Get Return _name End Get Set(ByVal Value As String) _name = Value End Set End Property Public Property Heimathafen() As String Get Return _heimathafen End Get Set(ByVal Value As String) _heimathafen = Value End Set End Property Public Property Baujahr() As Integer Get Return _baujahr End Get Set(ByVal Value As Integer) _baujahr = Value End Set End Property Public Property Kaufpreis() As Single Get Return _kaufpreis End Get Set(ByVal Value As Single) _kaufpreis = Value End Set End Property Public Property Stapellauf() As DateTime Get Return _stapellauf End Get Set(ByVal Value As DateTime) _stapellauf = Value End Set End Property Public Sub New(ByVal Name As String, ByVal Heimathafen As String, _ ByVal Baujahr As Integer, ByVal Kaufpreis As Single, ByVal Stapellauf As Date) Me.Name = Name Me.Heimathafen = Heimathafen Me.Baujahr = Baujahr Me.Kaufpreis = Kaufpreis Me.Stapellauf = Stapellauf End Sub Public Sub New() End Sub Public Class Comparer Implements IComparer Private Shared _sortProperties As System.ComponentModel.PropertyDescriptorCollection Private _sortProperty As System.ComponentModel.PropertyDescriptor Private _sortDescending As Boolean Public Sub New(ByVal sortFieldName As String, ByVal sortDescending As Boolean) If _sortProperties Is Nothing Then _sortProperties = System.ComponentModel.TypeDescriptor.GetProperties(GetType(Schiff)) End If _sortProperty = _sortProperties(sortFieldName) _sortDescending = sortDescending End Sub Public Sub New(ByVal sortFieldNumber As Integer, ByVal sortDescending As Boolean) If _sortProperties Is Nothing Then _sortProperties = System.ComponentModel.TypeDescriptor.GetProperties(GetType(Schiff)) End If _sortProperty = _sortProperties(sortFieldNumber) _sortDescending = sortDescending End Sub Public Function Compare(ByVal obj1 As Object, ByVal obj2 As Object) As Integer Implements IComparer.Compare Dim Wert1 As IComparable = CType(_sortProperty.GetValue(obj1), IComparable) Dim Wert2 As Object = _sortProperty.GetValue(obj2) Dim res As Integer = Wert1.CompareTo(Wert2) If _sortDescending Then Return -res Return res End Function End Class End Class Dim AL As New ArrayList Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' Erstellen einiger Instanzen Dim s1 As New Schiff("Albatros", "Rostock", 1864, 492.9, New Date(1863, 3, 5)) Dim s2 As New Schiff("Ubena von Bremen", "Bremen", 1380, 482.5, New Date(1379, 8, 27)) Dim s3 As New Schiff("Adler von Lübeck", "Lübeck", 1565, 285.7, New Date(1563, 12, 9)) Dim s4 As New Schiff("Rath von Riga", "Riga", 1341, 450.34, New Date(1340, 11, 24)) ' Füllen der ArrayList mit den Schiffen AL.Add(s1) AL.Add(s2) AL.Add(s3) AL.Add(s4) ' Show data DataGrid1.DataSource = AL End Sub Dim currentSortColumn As Integer = -1 Private Sub DataGrid1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles DataGrid1.Click Dim mp As Point = DataGrid1.PointToClient(MousePosition) Dim hti As DataGrid.HitTestInfo = DataGrid1.HitTest(mp.X, mp.Y) If hti.Type = DataGrid.HitTestType.ColumnHeader Then Dim sd As Boolean = hti.Column = currentSortColumn AL.Sort(New Schiff.Comparer(hti.Column, sd)) If sd Then currentSortColumn = -1 Else currentSortColumn = hti.Column End If End If DataGrid1.DataSource = AL End Sub End Class Dieser Tipp wurde bereits 11.941 mal aufgerufen. Voriger Tipp | Zufälliger Tipp | Nächster Tipp
Anzeige
Diesen und auch alle anderen Tipps & Tricks finden Sie auch auf unserer aktuellen vb@rchiv Vol.6 (einschl. Beispielprojekt!) Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv! - nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten - Symbol-Galerie mit mehr als 3.200 Icons im modernen Look Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m. |
sevISDN 1.0 Überwachung aller eingehender Anrufe! Die DLL erkennt alle über die CAPI-Schnittstelle eingehenden Anrufe und teilt Ihnen sogar mit, aus welchem Ortsbereich der Anruf stammt. Weitere Highlights: Online-Rufident, Erkennung der Anrufbehandlung u.v.m. Tipp des Monats März 2024 Dieter Otter UTF-8 Konvertierung von Dateien und Strings VB6 selbst verfügt über keine Funktionen zur UTF-8 Konvertierung von Daten. Mit Hilfe des ADODB.Stream-Objekts lassen sich diese fehlenden Funktionen aber schnell nachrüsten. sevOutBar 4.0 Vertikale Menüleisten á la Outlook Erstellen von Outlook ähnlichen Benutzer- interfaces - mit beliebig vielen Gruppen und Symboleinträgen. Moderner OfficeXP-Style mit Farbverläufen, Balloon-Tips, u.v.m. |
||||||||||||||||
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein. |