vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#
Top-Preis! AP-Access-Tools-CD Volume 1  
 vb@rchiv Quick-Search: Suche startenErweiterte Suche starten   RSS-Feeds  | Newsletter  | Impressum  | Datenschutz  | vb@rchiv CD Vol.6  | Shop Copyright ©2000-2017
 
zurück
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:     [ Jetzt bewerten ]Views:  8.996 

Summer-Special bei Tools & Components!
Gute Laune Sommer bei Tools & Components
Top Summer-Special - Sparen Sie teilweise bis zu 120,- EUR
Alle sev-Entwicklerkomponenten und Komplettpakete jetzt bis zu 25% reduziert!
zum Beispiel:
  • Developer CD nur 479,20 EUR statt 599,- EUR
  • sevDTA 3.0 nur 224,30 EUR statt 299,- EUR
  •  
  • vb@rchiv   Vol.6 nur 20,00 EUR statt 24,95 EUR
  • sevCoolbar 3.0 nur 55,20 EUR statt 69,- EUR
  • - Werbung -Und viele weitere Angebote           Aktionspreise nur für kurze Zeit gültig

    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

    Dieser Workshop wurde bereits 8.996 mal aufgerufen.

    Über diesen Workshop im Forum diskutieren
    Haben Sie Fragen oder Anregungen zu diesem Workshop, können Sie gerne mit anderen darüber in unserem Forum diskutieren.

    Neue Diskussion eröffnen

    nach obenzurück


    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.
     
       

    Druckansicht Druckansicht Copyright ©2000-2017 vb@rchiv Dieter Otter
    Alle 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.

    Diese Seiten wurden optimiert für eine Bildschirmauflösung von mind. 1280x1024 Pixel