vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#

https://www.vbarchiv.net
Rubrik: .NET   |   VB-Versionen: VB200802.02.09
Die Gestaltung von LING-Abfragen bei System.Arrays

In diesem Workshop finden Sie Erläuterungen, Beispiele und einige Erweiterungen zu LINQ-Abfragen bei System.Arrays. LINQ (Language Integrated Query) ist neu in VB2008 und stellt ein Konzept zur Verfügung, das Datenzugriffe aus verschiedenen Quellen mit einheitlichem VB-Code möglich macht.

Autor:  Manfred BohnBewertung:  Views:  13.170 

Mit der "Language Integrated Query" steht in VB2008 ein Konzept zur Verfügung, durch das der Zugriff auf Daten in verschiedenen Quellen durch einheitlichen VB-Code möglich ist.

Das funktioniert nicht nur bei externen Datenquellen, sondern kann auch bei im Hauptspeicher abgelegten Daten z.B. System.Arrays angewendet werden ('LINQ To Objects', d.h. ohne Verwendung eines LINQ-Anbieters).

Dieser Workshop kann nur einen kleinen Überblick über die Fülle der Möglichkeiten von 'LINQ To Object' bieten. Die Beispiele eignen sich zur ersten praktischen 'Kontaktaufnahme', sobald man die einführenden Abschnitte in der VB-Dokumentation gelesen hat.

Der Zugriff auf die von LINQ im Speicher erstellte Datensequenz erfolgt durch den Enumerator der generischen Schnittstelle IEnumerable(Of T), die im Namespace 'System.Collections.Generic' enthalten ist.

Um Linq-Abfragen bei System.Arrays über diese Schnittstelle zu verwenden, müssen sie 'queryable' sein, d.h. es werden die Erweiterungen benötigt, die im Namespace 'System.Linq.Enumerable' enthalten sind.

VB-Doku: Bei Methoden, die für Auflistungen im Arbeitsspeicher ausgeführt werden, also Methoden, die IEnumerable(Of (T)) erweitern, erfasst das zurückgegebene aufzählbare Objekt die an die Methode übergebenen Argumente. Wenn dieses Objekt aufgezählt wird, wird die Logik des Abfrageoperators angewendet, und die Abfrageergebnisse werden zurückgegeben.

Solche LINQ-Abfragen sind auch möglich bei mehrdimensionalen Arrays und bei Arrays, deren Elemente Instanzen eines Objekts sind.

Einige grundsätzliche Anmerkungen:

  1. Durch Verwendung von LINQ-Abfragen kann das Filtern, Sortieren, Neu-Berechnen, Transformieren oder Aggregieren der Daten in Arrays (nach ein wenig Einarbeitung) durch relativ einfachen VB-Code realisiert werden.
     
  2. In den Beispielen der LINQ-Dokumentation wird gewöhnlich der 'lokale Typrückschluss' verwendet (= inferentielle Deklarierung von Sequenz- und Bereichsvariablen). In den meisten Fällen ist das nicht notwendig.
     
  3. Bei der Verarbeitung von großen Datenmengen ist die Ausführungsgeschwindigkeit von LINQ-Abfragen etwas niedriger als die von (effizient gestaltetem!) VB-Code.
     
  4. Auf die von LINQ-Abfragen erstellte Datensequenz (Enumerable / 'Query') wird durch mitgelieferte oder selbsterstellte Erweiterungsmethoden zugegriffen. Man sollte bei der Benennung eigener 'Extensions' darauf achten, nicht versehentlich andere zu 'verdrängen'.
     
  5. Es ist zu unterscheiden zwischen der SPEICHERUNG einer LINQ-Abfrage in einer Sequenzvariable und der AUSFÜHRUNG der Abfrage (bei JEDEM Aufruf einer der LINQ-Erweiterungsmethoden oder beim Durchlaufen einer FOR-NEXT-Schleife = verzögerte Ausführung). Insbesondere bei voluminösen Arrays ist es deshalb zweckmäßig, die Datensequenz bzw. die Rückgabe von Erweiterungsmethoden in geeigneten Variablen zu speichern, falls mehrfach darauf zugegriffen werden soll.
     
  6. LINQ-Abfragen von Daten in externen Datenquellen (z.B. SQL-Datenbanken) verwenden spezielle Technologien ('LINQ-Anbieter'), durch die die LINQ-Anweisungen zunächst übersetzt werden. Es kommen dabei auch andere Schnittstellen zum Einsatz (z.B. 'IQueryable', vgl. Beispiel 11). Darauf geht dieser Workshop nicht ein!

Übersicht über die Beispiele zur Gestaltung von LINQ-Abfragen bei Daten in System.Arrays:

  1. Eindimensionales Double-Array (Filtern, Sortieren, Aggregieren)
  2. Berechnung von Variablen (Sequenz als anonymer Datentyp)
  3. Auswahl von Variablen (Select)
  4. Mehrdimensionale Arrays (Elemente vom Werttyp)
  5. Arrays, deren Elemente Instanzen eines Objekts sind
  6. Mehrdimensionale Arrays (Elemente als Referenztyp)
  7. Verknüpfung von Arrays (Join)
  8. Erweiterungsmethoden der 'IEnumerable'-Schnittstelle
  9. Die kontrollierte Verarbeitung von IEEE-Werten
  10. Selbst erstellte Aggregatfunktionen
  11. Die Nutzung der IQueryable-Schnittstelle
  12. Direkte Verwendung der Enumeratoren

Jedes Beispiel steht als Routine in einem Modul, damit auch die jeweils nötigen bzw. möglichen OPTION- und IMPORT-Anweisungen erkennbar werden. Um ein Beispiel auszuführen, erstellt man eine Windows-Anwendung und fügt ein Modul ein. Der in das Modul kopierte Code wird in der Form_Load-Routine des Startformulars als Routine aufgerufen z.B. durch 'Ex1'.

Es ist nur der NameSpace 'System' zu importieren. Alle weiteren Namespaces werden in den entsprechenden Modulen durch Imports-Statements oder explizit im Code einbezogen. Das soll der schnellen Orientierung im VB-'Objektraum' dienen.


Beispiel 1: Eindimensionales Double-Array
Bei der LINQ-Abfrage eines eindimensionalen Arrays kann die Bereichsvariable ('range_var') und die Sequenzvariable ('seq') explizit vereinbart werden. Die Anweisung 'OPTION INFER OFF' ist deshalb möglich.

Hat man mehrere LINQ-Abfragen in einer Function/Sub, dürfen sich die Bezeichnungen der Sequenzvariablen nicht wiederholen. Die Bereichsvariablen können nicht als lokale Variable der Function/Sub deklariert werden, sondern innerhalb der Abfrage.

Im Beispiel werden Array-Elemente gefiltert und fallend sortiert.Es entsteht eine 'in-memory-query' deren Elemente z.B. durch eine FOR-NEXT-Schleife abgefragt werden oder per Index durch die Default-Erweiterung 'ElementAt'.
(Zur Verwendung des Enumerators: vgl. Beispiel 11)

Die Erweiterung 'Count' informiert darüber, ob und wie viele Elemente die Query umfasst. Die Erweiterung 'ToArray' übertrõgt den Inhalt der Query in ein System-Array.

Die Aggregate-Klausel erlaubt die Berechnung von Kennwerten zu einer Query-Variable (im Beispiel: Berechnung der Summe der gefilterten Elemente durch die Standard-Aggregatfunktion 'Sum').

Bei der 'OrderBy'-Klausel ist allgemein zu beachten (VB-Doku):
Die aufsteigende bzw. absteigende Reihenfolge für ein Feld wird durch die Implementierung der IComparable-Schnittstelle für den Datentyp des Felds bestimmt. Wenn der Datentyp die IComparable-Schnittstelle nicht implementiert, wird die Sortierreihenfolge ignoriert.

Option Strict On
Option Explicit On
Option Infer Off
 
Imports System.Collections.Generic
Imports System.Linq.Enumerable
 
Module modLinqArray_Exempl01
 
  ''' <summary>Beispiele für Double-Array</summary>
  Public Sub Ex1()
 
    Dim dbl_arr(99) As Double, i As Integer
    For i = 0 To 99 : dbl_arr(i) = Rand() : Next i
 
    Const mn As Double = 0.25, mx As Double = 0.75
 
    Dim seq As IEnumerable(Of Double) = _
      From range_var As Double In dbl_arr _
      Where range_var > mn And range_var < mx _
      Order By range_var Descending
 
    If seq.Count < 1 Then Exit Sub
 
    Dim prev_elem As Double = 100
    Dim sum1 As Double = 0
    For Each elem As Double In seq
      sum1 += elem
      ' decend-Sortierung testen
      If elem > prev_elem Then Stop
      prev_elem = elem
    Next elem
 
    Dim sum2 As Double = 0
    For i = 0 To seq.Count - 1
      sum2 += seq(i)
      ' entspricht: sum2 += q1.ElementAt(i)
    Next i
 
    If Math.Abs(sum2 - sum1) > 0.0001 Then Stop
 
    ' Übertragung in ein Array
    Dim db_arr() As Double = seq.ToArray
    Dim sum3 As Double = 0
    For i = 0 To db_arr.Length - 1
      sum3 += db_arr(i)
    Next i
 
    If Math.Abs(sum3 - sum1) > 0.0001 Then Stop
 
    ' Verwendung der Standardaggregatfunktion
    Dim sum4 As Double = _
      Aggregate range_var As Double In dbl_arr _
      Where (range_var > mn And range_var < mx) _
      Into Sum()
 
    If Math.Abs(sum4 - sum1) > 0.0001 Then Stop
 
    Dim sum5 As Decimal = _
      Aggregate range_var As Double In dbl_arr _
      Where (range_var > mn And range_var < mx) _
      Let dec As Decimal = CDec(range_var) _
      Select dec Into Sum()
 
    If Math.Abs(sum5 - sum1) > 0.0001 Then Stop
 
  End Sub
 
End Module


Beispiel 2: Berechnung von Variablen
Es ist auch bei Arrays möglich, in der LINQ-Abfrage neue Variablen zu berechnen und sie in die 'Query' einzutragen (LET-Klausel). Sobald in der abgefragten Datensequenz mehr als eine Variable steht, handelt es sich bei der Sequenzvariable um einen 'anonymen Datentyp', der per lokalem Typ-Rückschluss zur Kompilierungszeit 'strikt' vereinbart wird. Das folgende Beispiel erfordert deshalb OPTION INFER ON.

Hinter der LET-Klausel kann die neu erstellte Variable ('new_var') in den folgenden Klauseln der Abfrage verwendet werden (im Beispiel: 'Order By'). Diese Variable kann einen anderen Datentyp besitzen als das Array - ggf. sind explizite Konvertierungen notwendig (z.B. 'CSng').

Die Erweiterungsmethode 'ToArray' erlaubt in diesem Fall die Übertragung des Inhalts der Datensequenz in ein Array des Typs 'Object'. Die Array-Elemente sind auch vom 'anonymen Typ', der die schreibgeschützten Eigenschaften 'range_var' und 'new_var' umfasst. Das gilt auch für die Schleifenkontrollvariable 'Elem'.

Im Beispiel verwendet die Sequenzvariable 'seq_sort' die Schnittstelle 'IOrderedEnumerable' (von 'IEnumerable' vererbt), weil das 'Order By'-Statement in der Abfrage benutzt wird. Diese Schnittstelle ist im Namespace 'System.Linq' vereinbart. Ihre explizite Deklaration ist nicht bei allen Abfragen sortierter Sequenzen möglich. Diese Schnittstelle erlaubt eine effizientere Ausführung nachher aufgerufener untergeordneter Such-Extensions.

Option Strict On
Option Explicit On
Option Infer On
 
Imports System.Linq.Enumerable
 
Module modLinqArray_Exempl02
 
  ''' <summary>Berechnung einer Variable
  ''' (anonyme Sequenzvariable)</summary>
  Public Sub Ex2()
 
    Dim dbl_arr(99) As Double, i As Integer
    For i = 0 To 99 : dbl_arr(i) = Rand() : Next i
 
    Const mn As Double = 0.25, mx As Double = 0.75
 
    Dim seq_anonym = _
      From range_var As Double In dbl_arr _
      Let new_var As Single = CSng(range_var * 10 * Rand()) _
      Where range_var >= mn And range_var <= mx _
      Order By new_var Descending
 
    ' explizit deklariertes Array
    Dim expl_arr() As Object = seq_anonym.ToArray
    ' Zugriff auf Array-Elemente erfordert 
    ' OPTION STRICT OFF (--> späte Bindung!)
    ' Dim vl As Double = expl_arr(0).range_var
    ' Dim v2 As Double = expl_arr(0).new_var
 
    ' inferentiell deklariertes Array (strikt)
    Dim impl_arr() = seq_anonym.ToArray
    ' ---> frühe Bindung
    Dim v1 As Double = impl_arr(0).range_var
    Dim v2 As Double = impl_arr(0).new_var
 
    Dim prev_elem As Single = 1000
    Dim sum As Double = 0
    For Each elem In seq_anonym
      ' anonymer Typ --> frühe Bindung
      sum += elem.range_var
      ' Sortierung testen
      If elem.new_var > prev_elem Then Stop
      prev_elem = elem.new_var
    Next elem
 
    ' Bei der Abfrage sortierter Daten ist in 
    ' vielen Fällen eine explizite Deklaration der
    ' IOrderedEnumerable'-Schnittstelle möglich 
    Dim seq_sort As Linq.IOrderedEnumerable(Of Double) = _
      From varia In dbl_arr _
      Where varia > 0.3 _
      Order By varia Descending
  End Sub
 
End Module


Beispiel 3: Auswahl von Variablen
Durch die SELECT-Klausel kann man auszuwählen, welche Variable(n) in die 'Query' eingetragen werden. Falls nur eine Variable übernommen wird, kann die Sequenzvariable explizit (gemäß dem Typ der berechneten Variable) deklariert werden (im Beispiel als 'Single'). Das Statement 'OPTION INFER OFF' ist zulässig.

Wenn man nur die Bereichsvariable in der Datensequenz benötigt und keine weitere Variable berechnet worden ist, kann die Select-KLausel weggelassen werden (vgl. Beispiel 1).

Option Strict On
Option Explicit On
Option Infer Off
 
Imports System.Collections.Generic
Imports System.Linq.Enumerable
 
Module modLinqArray_Exempl03
 
  ''' <summary>Berechnung einer Variable</summary>
  Public Sub Ex3()
 
    Dim dbl_arr(99) As Double, i As Integer
    For i = 0 To 99 : dbl_arr(i) = Rand() : Next i
 
    Const mn As Double = 0.25, mx As Double = 0.75
 
    ' Berechnung einer Variable des Typs 'Single'
    Dim seq As IEnumerable(Of Single) = _
      From range_var As Double In dbl_arr _
      Where range_var >= mn And range_var <= mx _
      Let new_var As Single = CSng(range_var * 10 * Rand()) _
      Order By new_var Descending _
      Select new_var
 
    Dim prev_elem As Single = 100
    Dim sum As Single = 0
    For Each elem As Single In seq
      sum += elem
      ' Sortierung testen
      If elem > prev_elem Then Stop
      prev_elem = elem
    Next elem
  End Sub
 
End Module


Beispiel 4: Mehrdimensionales Array
Beim Zugriff auf mehrdimensionale Arrays ist zu beachten, dass bei der Ausführung der Abfrage alle Array-Elemente durchlaufen werden - die entstehende Datensequenz ist aber 'eindimensional'. Eine explizite Deklaration der 'Query' und der Bereichsvariable kann als 'Object' erfolgen. In diesem Fall sind im Abfrage-Code geeignete Konvertierungsfunktionen erforderlich.

Eine typspezifische Deklaration von Sequenz- und Bereichsvariable erfordert die Verwendung der Erweiterung 'Cast' der 'IEnumerable'-Schnittstelle.

Im folgenden Beispiel führt die 'DISTINCT'-Klausel dazu, dass in der Sequenz keine Werte doppelt auftreten.

Option Strict On
Option Explicit On
Option Infer Off
 
Imports System.Collections.Generic
Imports System.Linq.Enumerable
 
Module modLinqArray_Exempl04
 
  ''' <summary>mehrdimensionales Array (Werttyp)</summary>
  Public Sub Ex4()
 
    Dim dbl_arr3(3, 13, 23) As Double
    For i As Integer = 0 To 3
      For k As Integer = 0 To 13
        For l As Integer = 0 To 23
          dbl_arr3(i, k, l) = Math.Round(Rand(), 1)
    Next l, k, i
 
    Const mini As Double = 0.25, maxi As Double = 0.55
 
    ' Mehrdimensionales Array wird als 'Object' verarbeitet
    Dim obj_seq As IEnumerable(Of Object) = _
      From range_var As Object In dbl_arr3 _
      Where CDbl(range_var) > mini And CDbl(range_var) < maxi _
      Order By range_var Ascending Distinct
 
    ' Mehrdimensionales Array wird 'gecastet'
    Dim dbl_seq As IEnumerable(Of Double) = _
      From range_var As Double In dbl_arr3.Cast(Of Double)() _
      Where range_var > mini And range_var < maxi _
      Order By range_var Ascending Distinct
 
    Dim sum1, sum2, sum3 As Double
    For Each elem As Double In obj_seq
      ' Filter testen
      If elem < mini Then Stop
      If elem > maxi Then Stop
      ' Summe durch Iteration
      sum1 += elem
    Next elem
 
    ' Berechnung der Summe durch Aggregatfunktion
    sum2 = Aggregate range_var As Object In dbl_arr3 _
      Where CDbl(range_var) > mini And CDbl(range_var) < maxi _
      Distinct Into Sum(CDbl(range_var))
 
    If Math.Abs(sum1 - sum2) > 0.0001 Then Stop
 
    sum3 = Aggregate range_var As Double _
      In dbl_arr3.Cast(Of Double)() _
      Where range_var > mini And range_var < maxi _
      Distinct Into Sum()
 
    If Math.Abs(sum1 - sum3) > 0.0001 Then Stop
 
  End Sub
 
End Module


Beispiel 5: Arrays, deren Elemente Instanzen eines Objekts sind
Zur Demonstration der LINQ-Abfragen bei Arrays, deren Elemente Objekte sind, wird eine Klasse verwendet, die einen Double-Wert kapselt ('cData') und im Konstruktor die Möglichkeit bietet, die Arrayindices der Instanz zu speichern. Die Implementierung der ICloneable-Schnittstelle erlaubt die Erstellung von Kopien einer Instanz.

''' <summary>Demo-Klasse für LINQ 2 Array</summary>
Public Class cData
 
  Implements ICloneable
 
  ' Membervariable der Klasse
  Dim _dbl As Double, _index1, _index2, _index3 As Integer
 
  ' Konstruktor
  Public Sub New(ByVal dbl As Double, _
    ByVal index1 As Integer, _
    Optional ByVal index2 As Integer = -1, _
    Optional ByVal index3 As Integer = -1)
 
    _dbl = dbl
    _index1 = index1 : _index2 = index2 : _index3 = index3
  End Sub
 
  ' Abfragen/Setzen des Wertes
  Public Property DBL() As Double
    Get
      Return _dbl
    End Get
    Set(ByVal value As Double)
      _dbl = value
    End Set
  End Property
 
  Public ReadOnly Property Index1() As Integer
    Get
      Return _index1
    End Get
  End Property
 
  Public ReadOnly Property Index2() As Integer
    Get
      Return _index2
    End Get
  End Property
 
  Public ReadOnly Property Index3() As Integer
    Get
      Return _index3
    End Get
  End Property
 
  ' Kopie der Objektinstanz
  Public Function Clone() As Object _
    Implements System.ICloneable.Clone
 
    Return Me.MemberwiseClone()
  End Function
 
End Class

Bei einem eindimensionalen Array, dessen Elemente Objektinstanzen (der gleichen Klasse) sind, kann die Sequenzvariable explizit deklariert werden. Die Methoden und Eigenschaften des Objekts stehen in der Sequenzvariable zur Verfügung.

Die 'in-memory-query' kann durch die Erweiterung 'ToArray' in ein Array eingetragen werden. Allerdings ist dabei zu beachten, dass die Array-Elemente Referenzen auf die Objekt-Instanzen des Quell-Arrays der LINQ-Abfrage sind. Im Beispiel würden sich Änderungen in dem Array 'ref_arr' auch auf die entsprechenden Elemente in dem Array 'obj_arr' auswirken.

Die Elemente in den Arrays 'arq_cl1' und 'arq_cl2' sind neu erstellte Objektinstanzen und verweisen nicht auf 'obj_arr'.

Das Resultat der Erweiterung 'Count' wird in einer Variable gespeichert, weil auf diesen Wert mehrfach zugegriffen wird.

Option Strict On
Option Explicit On
Option Infer Off
 
Imports System.Collections.Generic
Imports System.Linq.Enumerable
 
Module modLinqArray_Exempl05
 
  ''' <summary>Array mit Elementen vom Typ Object</summary>
  Public Sub Ex5()
 
    Dim obj_arr(99) As cData
    For i As Integer = 0 To 99
      obj_arr(i) = New cData(Rand, i)
    Next i
 
    Dim obj_seq As IEnumerable(Of cData) = _
      From dt As cData In obj_arr _
      Where dt.DBL < 0.25 Or dt.DBL > 0.75 _
      Order By dt.DBL Ascending
 
    Dim sum1 As Double = 0
    For Each elem As cData In obj_seq
      sum1 += elem.DBL
      ' Filter testen
      If elem.DBL >= 0.25 And elem.DBL <= 0.75 Then Stop
    Next elem
 
    ' Zahl der Elemente in der Sequenz speichern
    Dim N As Integer = obj_seq.Count
 
    ' Referenz auf Array-Elemente übertragen 
    Dim ref_arr() As cData = obj_seq.ToArray
 
    ' Array-Elemente anhand der Query neu erstellen 
    ' Nutzung des Konstruktors der Klasse 'cData'
    Dim arq_cl1(N - 1) As cData
    For i As Integer = 0 To N - 1
      With obj_seq(i)
        arq_cl1(i) = New cData(.DBL, .Index1, .Index2, .Index3)
      End With
    Next i
 
    ' Array-Elemente anhand der Query neu erstellen 
    ' Nutzung des Clone-Implementierung der Klasse 'cData'
    Dim arq_cl2(N - 1) As cData
    For i As Integer = 0 To N - 1
      arq_cl2(i) = CType(obj_seq(i).Clone, cData)
    Next i
 
    ' Summe der Arrayelemente berechnen
    Dim sum2 As Double = 0
    For i As Integer = 0 To ref_arr.Length - 1
      sum2 += ref_arr(i).DBL
    Next i
 
    If Math.Abs(sum1 - sum2) > 0.000001 Then Stop
 
    ' Summe per Aggregatfunktion berechnen
    Dim sum3 As Double = _
      Aggregate dt As cData In obj_arr _
      Where dt.DBL < 0.25 Or dt.DBL > 0.75 _
      Into Sum(dt.DBL)
 
    If Math.Abs(sum1 - sum3) > 0.000001 Then Stop
  End Sub
 
End Module


Beispiel 6: Mehrdimensionales Array, dessen Elemente Instanzen eines Objekts sind
Bei einem mehrdimensionalen Array kann die Sequenz- und die Bereichsvariable explizit vereinbart werden, wenn die 'Cast-Erweiterung verwendet wird.

Im Beispiel wird auf ein dreidimensionales Array zugegriffen. Die Array-Indices werden in den Objekt-Instanzen gespeichert. Auf diese Weise ist es möglich, bei den Verweisen in der Datensequenz die Array-Indices der Instanz abzufragen.

Die Elemente der 'Query' werden hierarchisch anhand der drei Index-Eigenschaften der Objekte sortiert.

Aus den Elementen in der Datensequenz werden geklonte Objektinstanzen als Elemente eines eindimensionalen Array ('cln_arq') erstellt.

Option Strict Off
Option Explicit On
Option Infer Off
 
Imports System.Collections.Generic
Imports System.Linq.Enumerable
 
Module modLinqArray_Exempl06
 
  ''' <summary>
  ''' Mehrdimensionales Array (Element: Objektreferenz)</summary>
  Public Sub Ex6()
 
    Dim obj_arr3(4, 5, 6) As cData
    For i As Integer = 0 To 4
      For k As Integer = 0 To 5
        For l As Integer = 0 To 6
          obj_arr3(i, k, l) = New cData(Rand, i, k, l)
    Next l, k, i
 
    Dim data_seq As IEnumerable(Of cData) = _
      From range_var As cData In obj_arr3.Cast(Of cData)() _
      Where CDbl(range_var.DBL) > 0.4 _
      Order By range_var.Index3 Descending, _
      range_var.Index2 Descending, _
      range_var.Index1 Descending
 
    Dim sum1 As Double = 0
    For Each elem As cData In data_seq
      sum1 += elem.DBL
      ' Filter prüfen
      If elem.DBL <= 0.4 Then Stop
      With elem
        ' Korrespondenz der Query mit dem Array prüfen
        If .DBL <> obj_arr3(.Index1, .Index2, .Index3).DBL Then Stop
      End With
    Next elem
 
    Dim N As Integer = data_seq.Count
 
    ' Array-Elemente anhand der Query neu erstellen 
    Dim cln_arr(N - 1) As cData
    For i As Integer = 0 To N - 1
      cln_arr(i) = data_seq(i).Clone
    Next i
 
    Dim sum2 As Double = 0
    For i As Integer = 0 To cln_arr.Length - 1
      sum2 += cln_arr(i).DBL
    Next i
 
    If Math.Abs(sum1 - sum2) > 0.000001 Then Stop
  End Sub
 
End Module


Beispiel 7: Verknüpfung von Arrays
Durch LINQ-Abfragen können auch Arrays miteinander verknüpft werden.

Eine Liste von Arrays im From-Abschnitt der Abfrage bewirkt die Ausführung einer geschachtelten Schleife. Der JOIN-Operator bewirkt, dass jedes Element im Array vor 'JOIN' mit jedem Element im Array nach 'JOIN' kombiniert wird.

Für 'Mengenvorgõnge' stehen Erweiterungen von 'IEnumerable' zur Verfügung (Except, Intersect, Union -- Rest-, Schnitt-, Vereinigungsmenge). Man beachte, dass diese Erweiterungen jedes unterscheidbare Element NUR EINMAL in die Sequenz übernehmen.

'Distinct' ist als VB-Schlüsselwort verfügbar.

Das folgende Beispiel liefert diejenigen Werte, die in beiden Arrays vorkommen (ohne Beachtung, ob auch die Array-Indices identisch sind!). Die Daten werden von der Erweiterung 'ToArray' SOFORT in ein Double-Array eingetragen, ohne dass die Deklaration einer Sequenzvariable und die Speicherung der Abfrage erforderlich ist.

Es wird ein Double- mit einem Single-Array kombiniert. Das Equals-Schlüsselwort funktioniert in diesem Fall nur korrekt, wenn beide Range-Variable vom Typ 'Single' sind. (Es handelt sich bei dieser Anwendung genau genommen um eine Zweckentfremdung, weil gewöhnlich nicht Daten-Werte, sondern 'Schlüssel' verglichen werden.) Für die Intersect-Erweiterung gilt das gleiche.

Möchte man die Elemente aus zwei Auflistungen indexbasiert kombinieren, kann man die Erweiterung 'JoinIndex' im Modul 'modLinqArray_Help' (Beispiel 9) verwenden. Sie erstellt eine Auflistung des Typs 'Object' deren Elemente anonyme Typen sind, die die zusammengeführten Array-Elemente enthalten.

Option Strict On
Option Explicit On
Option Infer Off
 
Imports System.Collections.Generic
Imports System.Linq.Enumerable
 
Module modLinqArray_Exempl07
 
  ''' <summary>Verknüpfung von Arrays</summary>
  Public Sub Ex7()
 
    Dim dbl_arr(9) As Double, i As Integer
    For i = 0 To dbl_arr.Length - 1
      dbl_arr(i) = Math.Round(Rand(), 1)
    Next i
 
    Dim sng_arr(19) As Single
    For i = 0 To sng_arr.Length - 1
      sng_arr(i) = CSng(Math.Round(Rand(), 1))
    Next i
 
    ' Die gespeicherte Abfrage kombiniert jedes Element 
    ' in dbl_arr mit allen Elementen in sng_arr 
    ' Die Sequenz enthielte deshalb 10*20 = 200 Elemente
    Dim dec_seq As IEnumerable(Of Decimal) = _
      From x As Double In dbl_arr, y As Single In sng_arr _
      Let z As Decimal = CDec(x + y) _
      Select z
 
    ' Das Zielarray ('db_arr') enthält eine sortierte Liste der 
    ' unterscheidbaren Werte, die in beiden Arrays vorkommen
    ' ToArray --> unmittelbare Ausführung der Abfrage
    ' Double muss in Single konvertiert werden!!
    Dim db_arr() As Double = _
      (From x As Double In dbl_arr _
      Distinct _
      Join y As Single In sng_arr _
      On CSng(x) Equals y _
      Distinct Order By x Select x).ToArray
 
    ' so geht es auch ...
    ' (Die zweite Query wird als Argument der Intersect-Erweiterung
    ' der ersten Query eingesetzt.)
    ' x muss hier in 'Single' konvertiert werden
    Dim d_arr() As Single = _
      ((From x As Double In dbl_arr _
      Let sng As Single = CSng(x) _
      Order By sng Select sng). _
      Intersect(From y As Single In sng_arr)).ToArray
 
    ' Kombination der Arrays (10 Elemente!)
    Dim obj_seq As IEnumerable(Of Object) = _
      dbl_arr.JoinIndex(sng_arr)
 
  End Sub
 
End Module


Beispiel 8: Erweiterungsmethoden der 'IEnumerable'-Schnittstelle
Die 'IEnumerable'-Schnittstelle bietet eine Fülle von 'Extensions', die eine direkte Weiterverarbeitung (z.B. Verknüpfen, Sortieren, Gruppieren, Filtern) der enthaltenen Daten erlauben. Für viele dieser Operationen steht kein entsprechendes VB-Schlüsselwort zur Verfügung (vgl. Beispiel 7). Näheres dazu im Kapitel 'Übersicht über Standardabfrageoperatoren' in der VB-Dokumentation.

Die Erweiterung 'ToQuery' (im Modul 'modLinqArray_Help' - siehe Beispiel 9) überträgt die Elemente eines Array in eine 'Query'.

Option Strict On
Option Explicit On
Option Infer Off
 
Imports System.Collections.Generic
Imports System.Linq.Enumerable
 
Module modLinqArray_Exempl08
 
  ''' <summary>
  ''' Erweiterungen der IEnumerable-Schnittstelle</summary>
  Public Sub Ex8()
 
    Dim int_arr1(9) As Integer, int_arr2(9) As Integer
    For i As Integer = 0 To 9
      int_arr1(i) = CInt(Rand() * 10)
      int_arr2(i) = CInt(Rand() * 10)
    Next i
 
    ' Die Arrays in Sequenzen eintragen
    '  ---> per Linq-Abfrage
    Dim int_seq1 As IEnumerable(Of Integer) = _
      From range_var As Integer In int_arr1
    ' ---> per Erweiterung von System.Array
    ' vereinbart im Modul 'modLinqArray_Help'
    Dim int_seq2 As IEnumerable(Of Integer) = _
      int_arr2.ToQuery
 
    ' 2. Sequenz an 1. Sequenz hinten anhängen
    Dim kombi_seq As IEnumerable(Of Integer) = _
      int_seq1.Concat(int_seq2)
    ' nur unterscheidbare Elemente übernehmen
    Dim dist_seq As IEnumerable(Of Integer) = kombi_seq.Distinct
    ' Query sortieren
    Dim sort_seq As System.Linq.IOrderedEnumerable(Of Integer) = _
      dist_seq.OrderBy(Function(x As Integer) x)
    ' alle Werte <= 4 in der sortierten Sequenz aussondern
    Dim seq As IEnumerable(Of Integer) = _
      sort_seq.SkipWhile(Function(x As Integer) x <= 4)
  End Sub
 
End Module


Beispiel 9: Die kontrollierte Verarbeitung von IEEE-Werten (selbsterstellte Erweiterungen)
Bei der Verwendung von LINQ-Klauseln bei Gleitkomma-Variablen sind die üblichen Vorsichtsma¯nahmen erforderlich (IEEE-Sonderwerte, keine Ausnahmen). Durch Verwendung geeigneter Erweiterungsmethoden lassen sich aber Probleme vermeiden bzw. abfangen.

Im Beispiel wird die Erweiterung 'IsValid' zum Aussortieren von IEEE-Sonderwerten im abgefragten Array verwendet.

Die Erweiterung 'Check' erlaubt das Auslösen einer Ausnahme beim Überlauf oder bei Double.NaN-Werten, wenn Variable neu berechnet werden. Die Erweiterungen sind in dem Modul 'modLinqArray_Help' vereinbart.

Man beachte, dass Ausnahmen erst ausgelöst werden, sobald die Datensequenz in der FOR-NEXT-Schleife weiterverarbeitet wird (verzögerte Ausführung). Bei der Speicherung der LINQ-Abfrage in der Sequenzvariable geschieht zunächst nichts.

Option Strict On
Option Explicit On
Option Infer Off
 
Imports System.Collections.Generic
Imports System.Linq.Enumerable
 
Module modLinqArray_Exempl09
 
  ''' <summary>IEEE-Sonderwerte behandeln</summary>
  Public Sub Ex9()
 
    ' Array mit einigen Sonderwerten füllen
    Dim dbl_arr(99) As Double
    For i As Integer = 0 To 99
      If i Mod 5 = 0 Then
        dbl_arr(i) = Double.NaN
      ElseIf i Mod 7 = 0 Then
        dbl_arr(i) = Double.NegativeInfinity
      Else
        dbl_arr(i) = Math.Round(Rand() * 10)
      End If
    Next i
 
    ' Beispiel: Filtern invalider der Werte im Array
    Dim seq_valid As IEnumerable(Of Double) = _
      From range_var In dbl_arr _
      Where range_var.IsValid And range_var > 5
 
    ' Beispiel: Überwachung der Berechnung von 'new_var'
    Dim seq_check As IEnumerable(Of Double) = _
      From range_var In dbl_arr _
      Where range_var.IsValid _
      Let new_var As Double = (100.0 / range_var).check _
      Select range_var
 
    ' Bei Ausführung der Schleife tritt die Ausnahme auf:
    ' wenn 'range_var' den Wert 0 enthält, wird 'elem' unendlich
    Dim sum As Double = 0
    For Each elem As Double In seq_check
      sum += elem
    Next elem
  End Sub
 
End Module
Option Strict On
Option Explicit On
Option Infer Off
 
Imports System.Collections.Generic
Imports System.Linq.Enumerable
 
''' <summary>
''' Hilfsfunktionen f³r LINQ-Abfragen bei System.Arrays</summary>
Module modLinqArray_Help
 
  ''' <summary>
  ''' Überprüfung von numerischen Werten
  ''' Bereich Double.MinValue - Double.MaxValue</summary>
  ''' <param name="value">zu prüfender Wert</param>
  ''' <returns>Wert im Geltungsbereich Double?</returns>
  <System.Runtime.CompilerServices.Extension()> _
  Public Function IsValid(Of T As Structure) (ByVal value As T) As Boolean
    Try
      Dim dbl As Double = Convert.ToDouble(value)
      Return dbl >= Double.MinValue And _
      dbl <= Double.MaxValue
    Catch
      Return False
    End Try
  End Function
 
 
  ''' <summary>Erweiterung löst eine Ausnahme aus, 
  ''' falls Double-Wert ausserhalb von MinValue, MaxValue
  ''' </summary>
  ''' <param name="value">zu prüfender Wert</param>
  <System.Runtime.CompilerServices.Extension()> _
  Public Function check(Of T As Structure) (ByVal value As T) As T
    Dim dbl As Double = Convert.ToDouble(value)
    If Double.IsNaN(dbl) Then
      Throw New InvalidCastException
    End If
    If Double.IsInfinity(dbl) Then
      Throw New OverflowException
    End If
    Return value
  End Function
 
 
  ''' <summary>
  ''' Übertragung der Elemente eines eindimensionalen Array
  ''' (Elemente = Werttyp) in eine 'Query'</summary>
  ''' <typeparam name="T">Typ der Array-Elemente</typeparam>
  ''' <param name="arr">einzutragendes Array</param>
  ''' <returns>Eine 'Query' vom Typ der Arrayelemente</returns>
  <Runtime.CompilerServices.Extension()> _
  Public Function ToQuery(Of T As Structure)(ByVal arr As T()) As IEnumerable(Of T)
    Return From x In arr
  End Function
 
 
  ''' <summary>
  ''' Übertragung der Elemente eines zweidimensionalen Array
  ''' (Elemente = Werttyp) in eine 'Query'</summary>
  ''' <typeparam name="T">Typ der Array-Elemente</typeparam>
  ''' <param name="arr">einzutragendes Array</param>
  ''' <returns>Eine 'Query' vom Typ der Arrayelemente</returns>
  <Runtime.CompilerServices.Extension()> _
  Public Function ToQuery(Of T As Structure)(ByVal arr As T(,)) As IEnumerable(Of T)
    Return (From elem As Object In arr).Cast(Of T)()
  End Function
 
 
  ''' <summary>
  ''' Übertragung der Elemente eines dreidimensionalen Array
  ''' (Elemente = Werttyp) in eine 'Query'</summary>
  ''' <typeparam name="T">Typ der Array-Elemente</typeparam>
  ''' <param name="arr">einzutragendes Array</param>
  ''' <returns>Eine 'Query' vom Typ der Arrayelemente</returns>
  <Runtime.CompilerServices.Extension()> _
  Public Function ToQuery(Of T As Structure)(ByVal arr As T(,,)) As IEnumerable(Of T)
    Return (From elem As Object In arr).Cast(Of T)()
  End Function
 
 
  '''' <summary>
  '''' Übertragung der Elemente eines eindimensionalen 
  '''' Array in eine 'Query'</summary>
  '''' <typeparam name="T">Typ der Array-Elemente</typeparam>
  '''' <param name="arr">einzutragendes Array</param>
  '''' <returns>Eine 'Query' vom Typ der Arrayelemente</returns>
  <Runtime.CompilerServices.Extension()> _
  Public Function EnumToQuery(Of T) (ByVal arr As T()) As IEnumerable(Of T)
    Return (From elem As Object In arr).Cast(Of T)()
  End Function
 
 
  ''' <summary>
  ''' Indexbasierte Kombination zweier Auflistungen</summary>
  ''' <typeparam name="T1">Datentyp 1. Auflistung</typeparam>
  ''' <typeparam name="T2">Datentyp 2. Auflistung</typeparam>
  ''' <param name="data1">Erste Auflistung</param>
  ''' <param name="data2">Zweite Auflistung</param>
  ''' <returns>Kombinierte Auflistung</returns>
  <Runtime.CompilerServices.Extension()> _
  Public Function JoinIndex(Of T1, T2) (ByVal data1 As IEnumerable(Of T1), _
    ByVal data2 As IEnumerable(Of T2)) As IEnumerable(Of Object)
 
    Dim N As Integer = data1.Count
    Dim N2 As Integer = data2.Count
    N = Math.Min(N, N2)
    Dim ao(N - 1) As Object
    For i As Integer = 0 To N - 1
      ao(i) = New With {.value1 = data1(i), .value2 = data2(i)}
    Next i
 
    Return From x In ao
  End Function
 
 
  ''' <summary>Zufallszahl im Bereich 0--1</summary>
  ''' <returns>Zufallszahl (per Rnd)</returns>
  Public Function Rand() As Double
    Return Microsoft.VisualBasic.Rnd
  End Function
 
End Module


Beispiel 10: selbsterstellte Aggregatfunktionen (Standardabweichung, Median)
Eine 'Aggregatfunktion' wird auf die abgefragte Datensequenz angewendet und berechnet aus allen darin enthaltenen Daten einen einzelnen Kennwert (Singleton).

Die Sequenzvariable des Typs 'IEnumerable' ist um eine Reihe von Standard-Aggregatfunktionen erweitert, die aber bei IEEE-Variablen die Sonderwerte nicht in allen Fällen sinnvoll verarbeiten. Zweckmäßiger ist es deshalb, eigene Funktionen für die Aggregate-Klausel bei LINQ-Abfragen zu erstellen.

Die Beispiel-Funktion 'SDEV' berechnet die Daten-Standardabweichung (N-Gewichtung) der Elemente ein- oder mehrdimensionaler Arrays eines beliebigen numerischen Datentyps, die Funktion 'Median' den Medianwert der Daten. Die Erweiterung 'Count_Missings' gibt die Zahl der Array-Elemente an, die IEEE-Sonderwerte enthalten. 'Count_Range' gibt die Zahl der Elemente an, die innerhalb eines vorgegebenen Werte-Intervalls liegen.

Um die Funktionen für alle numerischen 'Primitives' verfügbar zu machen, ist die Hilfsfunktion 'ToDoubleArray' erstellt worden. Sie filtert unplausible IEEE-Werte und benutzt eine Methode der System.Convert-Klasse zur Konvertierung.

Die Berechnungen werden deshalb stets mit Double-Werten durchgeführt. Falls eine Funktion scheitert, wird 'NaN' bzw. -1 zurückgegeben.

Die Erweiterungen sind auch direkt auf eindimensionale numerische Arrays anwendbar (vgl. Aufruf-Beispiel). Diese 'Extensions' werden auch für Variablen des Typs 'System.String' (=Character-Array) von der VB-Intellisense angezeigt (Werttyp). Einfach ignorieren! Die Convert-Methode löst beim Datentyp 'Char' immer eine Ausnahme aus.

Im Aufruf-Beispiel werden durch die Erweiterung 'ToQuery' alle Elemente eines zweidimensionalen Single-Arrays in eine Sequenz eingetragen, die dann durch 'SDEV' bzw. 'Median' aggregiert wird.

Die hier vorgeschlagene Variante unterscheidet sich von der in der VB-Dokumentation (Modul 'UserDefinedAggregates'). Soweit ich beurteilen kann, ist das dortige Beispiel (Aufruf einer Selector-Funktion) nur in LINQ-Abfragen, aber nicht als Erweiterung für Arrays verwendbar (Ausnahme: Datentyp Double, Direktaufruf).

Option Strict On
Option Explicit On
Option Infer Off
 
Imports Microsoft.VisualBasic.Information 'IsNothing
Imports System.Collections.Generic
Imports System.Linq.Enumerable
 
''' <summary>
''' Modul mit Aggragatfunktion für LINQ-Aggregate</summary>
Module modLinqArray_Statistik
 
  ''' <summary>Berechnung der Standardabweichung der 
  ''' gültigen numerischen Werte in der Auflistung 
  ''' </summary>
  ''' <param name="enm_data">Auflistung</param>
  ''' <returns>Standardabweichung (oder NaN!)</returns>
  <Runtime.CompilerServices.Extension()> _
  Public Function SDEV(Of T As Structure) (ByVal enm_data As IEnumerable(Of T)) As Double
    Try
      ' Auswahl der gültigen Werte
      ' incl. Umwandlung in Double-Werte
      Dim iarr() As Double = ToDoubleArray(enm_data)
      If IsNothing(iarr) Then Return Double.NaN
      Dim N As Integer = iarr.Length
      If N < 1 Then Return Double.NaN
 
      ' Mittelwert der gültigen Daten
      ' durch Erweiterung von 'Enumerable'
      Dim mw As Double = iarr.Average
 
      ' Summe der Abweichungsquadrate
      ' (starkes Anwachsen der Summe vermeiden)
      Dim sum As Double = 0
      For Each elem As Double In iarr
        sum += ((elem - mw) ^ 2 / N)
      Next elem
 
      ' Rückgabe der Standardabweichung 
      Return Math.Sqrt(sum)
    Catch ex As Exception
      Return Double.NaN
    End Try
  End Function
 
 
  ''' <summary>Berechnung des Medianwertes der 
  ''' gültigen numerischen Werte in der Auflistung 
  ''' </summary>
  ''' <param name="enm_data">Auflistung</param>
  ''' <returns>Median (oder NaN!)</returns>
  <Runtime.CompilerServices.Extension()> _
  Public Function Median(Of T As Structure) (ByVal enm_data As IEnumerable(Of T)) As Double
    Dim iarr() As Double = ToDoubleArray(enm_data)
    If IsNothing(iarr) Then Return Double.NaN
    If iarr.Length = 0 Then Return Double.NaN
 
    ' gültige Daten sortieren
    Array.Sort(iarr)
    ' mittleren Index ermitteln
    Dim mitte As Integer = iarr.Length \ 2
    ' Übliche Konvention für Medianberechnung 
    If iarr.Length Mod 2 = 0 Then
      Return (iarr(mitte) + iarr(mitte - 1)) / 2
    Else
      Return iarr(mitte)
    End If
  End Function
 
 
  ''' <summary>Anzahl der ungültigen/fehlenden 
  ''' numerischen Werte in der Auflistung</summary>
  ''' <param name="enm_data">Auflistung</param>
  ''' <returns>Anzahl ungültiger Werte (oder -1)</returns>
  <Runtime.CompilerServices.Extension()> _
  Public Function Count_Missings(Of T As Structure) (ByVal enm_data As IEnumerable(Of T)) As Integer
    ' Auswahl der gültigen Werte
    ' incl. Umwandlung in Double-Werte
    Dim N As Integer = enm_data.Count
    Dim iarr() As Double = ToDoubleArray(enm_data)
    If IsNothing(iarr) Then Return -1
    Return N - iarr.Length
  End Function
 
 
  ''' <summary>Anzahl der Werte innerhalb eines Bereichs
  ''' </summary>
  ''' <typeparam name="T">Datentyp</typeparam>
  ''' <param name="enm_data">Auflistung</param>
  ''' <param name="Range_Minimum">Untergrenze des Bereichs</param>
  ''' <param name="Range_Maximum">Obergrenze des Bereichs</param>
  ''' <returns>Anzahl im Bereich (oder -1)</returns>
  <Runtime.CompilerServices.Extension()> _
  Public Function Count_Range(Of T As Structure) (ByVal enm_data As IEnumerable(Of T), _
    ByVal Range_Minimum As T, ByVal Range_Maximum As T) As Integer
 
    Const minuseins As Integer = -1
    Try
      Dim Range_Mini As Double = Convert.ToDouble(Range_Minimum)
      Dim Range_Maxi As Double = Convert.ToDouble(Range_Maximum)
      If Range_Mini > Range_Maxi Then Return minuseins
 
      ' Auswahl der gültigen Werte
      ' incl. Umwandlung in Double-Werte
      Return (ToDoubleArray(enm_data, Range_Mini, Range_Maxi).Length)
    Catch
      Return minuseins
    End Try
  End Function
 
 
  ''' <summary>
  ''' Filtert und Konvertiert eine Auflistung</summary>
  ''' <param name="enm_data">Auflistung</param>
  ''' <returns>Double-Array (oder Nothing)</returns>
  Private Function ToDoubleArray(Of T As Structure) (ByVal enm_data As IEnumerable(Of T), _
    Optional ByVal MinVal As Double = Double.MinValue, _
    Optional ByVal MaxVal As Double = Double.MaxValue) As Double()
 
    Try
      Return (From x In enm_data _
        Let dbl As Double = Convert.ToDouble(x) _
        Where dbl >= MinVal And dbl <= MaxVal _
        Select dbl).ToArray
    Catch
      Return Nothing
    End Try
  End Function
 
End Module
Option Strict On
Option Explicit On
Option Infer Off
 
Imports System.Collections.Generic
Imports System.Linq.Enumerable
 
Module modLinqArray_Exempl10
 
  ''' <summary>Beispiele zur Verwendung von 
  ''' selbsterstellten Aggregatfunktionen</summary>
  Public Sub Ex10()
 
    ' Zweidimensionales Array mit einigen NaN-Werten
    Dim sng_arr2(99, 3) As Single
    For i As Integer = 0 To 99
      For k As Integer = 0 To 3
        sng_arr2(i, k) = CSng(i)
        If i Mod 5 = 0 Then
        sng_arr2(i, k) = Single.NaN
      End If
    Next k, i
 
    ' Berechnung der Standardabweichung
    ' der gültigen Werte > 50 im Array durch die
    ' Aggregatfunktion 'SDev' 
    Dim sd As Double = _
      Aggregate range_var As Single In sng_arr2.ToQuery _
      Where range_var > 50 Into SDEV()
 
    ' Berechnung des Median
    ' der gültigen Werte <= 50 im Array durch die
    ' Aggregatfunktion 'Median' 
    Dim med As Double = _
      Aggregate range_var As Single In sng_arr2.ToQuery _
      Where range_var <= 50 Into Median()
 
    ' Eindimensionales Array
    Dim sng_arr(99) As Single
    For i As Integer = 0 To 99
      sng_arr(i) = CSng(Rand())
    Next i
 
    ' Die Aggregate-Funktionen operieren auch als
    ' Erweiterungen eindimensionaler Arrays
    Dim sd_arr As Double = sng_arr.SDEV
    Dim med_arr As Double = sng_arr.Median
    Dim cr_arr As Integer = sng_arr.Count_Range(0.2, 0.7)
  End Sub
 
End Module


Beispiel 11: Nutzung der IQueryable-Schnittstelle
Für die Abfrage von Daten in Arrays wird die (generische) IQueryable-Schnittstelle nicht benötigt. Man kann sie aber aus der IEnumerable-Schnittstelle per Transformation (Erweiterung 'AsQueryable') erhalten. Die Sequenz ist dann kompatibel zu 'Queries', die auf externe Datenquellen verweisen.

Die Schnittstelle 'IQueryable' ist NICHT im Namespace System.Collections.Generic' angesiedelt, sondern im Namespace 'System.Linq'.

Ihre Erweiterungsmethoden im Namespace 'System.Linq.Queryable' entsprechen weitgehend denen für die 'IEnumerable'-Schnittstelle, die in 'System.Linq.Enumerable' vereinbart sind. Vieles ist geerbt.

Es wird gezeigt, wie die Datensequenz durch Verwendung des Enumerators (Methode 'MoveNext', schreibgeschützte Eigenschaft 'Current') durchlaufen werden kann. Die Reset-Methode erlaubt den Neubeginn der Iteration.

Option Strict On
Option Explicit On
Option Infer Off
 
Imports Microsoft.VisualBasic.Strings 'Format, Replace
Imports System.Collections.Generic
Imports System.Linq.Enumerable
Imports System.Linq.Queryable
 
Module modLinqArray_Exempl11
 
  '''<summary>
  '''Sequenzvariable über IQueryable-Schnittstelle</summary>
  Public Sub Ex11()
 
    Dim str_arr(99) As String
    For i As Integer = 0 To 99
      str_arr(i) = "Element " + Format(i, "00")
    Next i
 
    Dim seq_quer As Linq.IQueryable(Of String) = _
      (From str As String In str_arr _
      Let newstr As String = Replace(str, "Element", "-->") _
      Order By newstr Descending Select newstr).AsQueryable
 
    ' System.Linq.EnumerableQuery`1[System.String]
    Dim typ_quer As String = seq_quer.GetType.ToString
 
    ' IQueryable erbt den Enumerator von IEnumerable 
    Dim enm_quer As IEnumerator(Of String) = _
      seq_quer.GetEnumerator()
 
    Dim el_quer As String
    While enm_quer.MoveNext()
      el_quer = enm_quer.Current
    End While
  End Sub
 
End Module


Beispiel 12: Direkte Verwendung der Enumeratoren
Bei der Verwendung der Enumeratoren ist zu beachten, dass mit der 'MoveNext'-Methode begonnen werden muss, weil die Eigenschaft 'Current' sonst noch keinen sinnvollen Wert enthält.

Die Methode 'Reset' setzt den Enumerator auf den Anfang zurück.

Es ist nur die einfache Iteration der Elemente eines Array oder einer Query möglich. Um die aktuelle Position in der Datensequenz zu erhalten, muss eine Zählvariable mitgeführt werden.

Das Modul 'modLinqArray_Help' wird benötigt. Die Erweiterung 'EnumToQuery' trägt Array-Elemente in eine Sequenz ein und ist nicht auf Werttypen beschränkt.

Option Strict On
Option Explicit On
Option Infer Off
 
Imports System.Collections
Imports System.Collections.Generic
 
Module modLinqArray_Exempl12
 
  ''' <summary>
  ''' Verwendung der Enumeratoren: System.Collections
  ''' und System.Collections.Generic</summary>
  Public Sub Ex12()
 
    Dim sng_arr(99) As Single
    For i As Integer = 0 To 99
      sng_arr(i) = CSng(Rand()) + CSng(i)
    Next i
 
    ' generische Schnittstelle (Werttyp)
    Dim enm_sng As IEnumerator(Of Single) = _
      sng_arr.ToQuery.GetEnumerator
 
    Dim sng As Single
    Dim z As Integer = -1 ' Zählvariable/null-basiert
    With enm_sng
      While .MoveNext
        z += 1
        sng = .Current
      End While
    End With
 
    Dim arr_str(99) As String
    For i As Integer = 0 To 99
      arr_str(i) = "Element " + Microsoft.VisualBasic.Format(i, "000")
    Next i
 
    ' generische Schnittstelle
    Dim enm_str As IEnumerator(Of String) = _
      arr_str.EnumToQuery.GetEnumerator
 
    Dim str As String
    With enm_str
      While .MoveNext
        str = .Current
      End While
    End With
 
    ' nicht-generische Schnittstelle
    Dim enm_arr As IEnumerator = arr_str.GetEnumerator
    With enm_arr
      While .MoveNext
        str = CStr(.Current)
      End While
    End With
 
    Dim obj_arr(99) As Object
    For i As Integer = 0 To 99
      obj_arr(i) = New cData(Rand, i)
    Next i
 
    ' generische Schnittstelle
    Dim enm_obj As IEnumerator(Of Object) = _
      obj_arr.EnumToQuery.GetEnumerator
 
    Dim dt As cData
    With enm_obj
      While .MoveNext
        dt = CType(.Current, cData)
      End While
    End With
  End Sub
 
End Module



Anzeige

Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Workshops 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.
 
 
Copyright ©2000-2024 vb@rchiv Dieter OtterAlle Rechte vorbehalten.


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.