none
Object-Array 'Object[]' richtig verwenden RRS feed

  • Frage

  • Hallo Forum!

    ich habe ein Problem bei der Speicherung von Objekten vom Typ System.IO.Ports.SerialPort in einem Object-Array, wenn ich diesen mit 'Add-Member' Properties hinzufüget habe. Ich zeige hier mal was ich meine:

    Function OpenComPorts-ForGrblConnection {
    param (
        [ValidateSet(300, 600, 1200, 1800, 2400, 4800, 7200, 9600, 14400, 19200, 38400, 57600, 115200, 230400, 460800, 921600)] [Int] $Baudrate = 115200,
        [ValidateSet(7, 8)] [Int] $DataBits = 8,
        [ValidateSet('Even', 'Mark', 'None', 'Odd', 'Space')] [String] $Parity   = 'None',
        [ValidateSet('None', 'One', 'OnePointFive', 'Two')]   [String] $StopBits = 'One'
    )
        $PortNames = [System.IO.Ports.SerialPort]::GetPortNames()
        $A = New-Object 'Object[]' $PortNames.Count
        For ($i=0; $i -le $PortNames.Count - 1; $i++) {
            $c = New-Object -TypeName System.IO.Ports.SerialPort $Port,$Baudrate,$Parity,$Databits,$StopBits
            #$A[$i] = $c
            $c.PortName = $PortNames[$i]
            $c.Open()
            If ($c.IsOpen) {
                $c.WriteLine('$I')
                $Response = $c.ReadExisting()
                $Response = $Response -split "\r\n"
                If ($Response[1] -eq "ok") {
                    Add-Member -InputObject $c -MemberType NoteProperty -Name "GrblOk" -Value $true #-TypeName Bool
                    If ($Response[0] -match "\d{8}") {
                        $BuildDate = [System.DateTime]::ParseExact($Matches[0],"yyyyMMdd", $null)
                        Add-Member -InputObject $c -MemberType NoteProperty -Name "BuildDate" -Value $BuildDate #-TypeName DateTime
                        If ($Response[0] -match "\d{1}\.\d{1}[a-z]{1}") {
                            Add-Member -InputObject $c -MemberType NoteProperty -Name "BuildVersion" -Value ($Matches[0]) #-TypeName String
                            $A[$i] = $c
                        }
                    }
                } else {
                    $c.Close()
                }
            }
        }
        Return $A
    }

    Okay - der Code lässt sich nur sinnvoll mit entsprechender Hardware am Comport betreiben, aber das Prinzip sollte klar sein. Es werden nacheinander alle Comports geöffnet und bei der Kommunikation bestimmte Antworten erwartet. Add-Member fügt entsprechend NoteProperties zu den Serial-Objekten hinzu. Die Objektereferenz befinden sich zu dem Zeitpunkt noch nicht im Array, aber wenn ich die Zuweisung '$A[$i] = $c' mache gerät alles durcheinander.

    Vermutlich mache ich mit dem Array etwas Grundsätzliches falsch. Hat jemand die Checkung :-)



    Michael L.

    Sonntag, 11. September 2016 12:36

Antworten

  • Hallo,

    das mit der festen Groesse habe ich irgendwie ueberlesen, es sollte aber auch gehen, das Array $a leer ohne feste Groesse zu definieren und dann im IF Zweig

    $a += $c

    Beste Groesse
    brima

    Sonntag, 11. September 2016 20:11
  •  

    > Das passt.

    Ja, aber... ;) Man sollte dazu wissen, das beim hinzufügen mit "+=" Powershell im Speicher jedesmal zuerst ein komplett neues Array mit allen Elementen des alten Arrays + dem jeweils neu hinzugefügten Element erzeugt und dann erst das alte Array überschreibt. Das liegt daran, das Arrays per Definition Elementgruppen mit fest definierter Größe sind.

    Diese Methode ist bei 5-10 Elementen egal, bei Hunderten und mehr, sehr, sehr langsam. Unter Umständen kann sogar der Speicher zum Problem werden.

    Lösung: Eine dynamische Liste statt dessen nehmen.

    $meinArray = New-Object System.Collections.Generic.List[System.Object]
    $meinArray.add($meinElement)  oder   
    $meinArray.add("Test")

     
    Neben einer nun sehr effizenten add()-Methode kannst du damit sogar Elemente wieder entfernen, mit remove().


    Grüße, Denniver


    Blog: http://bytecookie.wordpress.com

    Neu: Powershell Code Manager v5 ! Link
    (Schneller, besser + komfortabler scripten.)

    Hilf mit und markiere hilfreiche Beiträge mit dem "Abstimmen"-Button (links) und Beiträge die eine Frage von dir beantwortet haben, als "Antwort" (unten).
    Warum das Ganze? Hier gibts die Antwort.

    Montag, 12. September 2016 13:03
    Moderator

Alle Antworten

  • Hallo,

    dann versuche es mal mit:

    $A[$i] += $c

    siehe auch

    Get-Help about_Arrays

    Beste Gruesse
    brima


    • Bearbeitet brima Sonntag, 11. September 2016 13:03
    Sonntag, 11. September 2016 13:00
  • Hallo brima,

    hab jetzt noch ein wenig rumprobiert. Mit '+=' geht es nicht, da dass Array eine feste Größe hat. Wenn ich aber auch die sonst leer gelassenen Array-Elemente mit den unbenutzten Com-Ports befülle, klapts.

                } else {
                    $c.Close()
                    $A[$i] = $c
                }
    

    Vielen Dank dennoch!


    Michael L.

    Sonntag, 11. September 2016 19:56
  • Hallo,

    das mit der festen Groesse habe ich irgendwie ueberlesen, es sollte aber auch gehen, das Array $a leer ohne feste Groesse zu definieren und dann im IF Zweig

    $a += $c

    Beste Groesse
    brima

    Sonntag, 11. September 2016 20:11
  • Feste Größe is doch sowieso des Teufels ;-) Zumal Du sie eh zur Laufzeit ermittelst, indem Du die Ports ausliest...

    Evgenij Smirnov

    msg services ag, Berlin -> http://www.msg-services.de
    my personal blog (mostly German) -> http://it-pro-berlin.de
    Windows Server User Group, Berlin -> http://www.winsvr-berlin.de
    Mark Minasi Technical Forum, reloaded -> http://newforum.minasi.com

    In theory, there is no difference between theory and practice. In practice, there is.

    Sonntag, 11. September 2016 20:19
  • dann also so:

    $A = @()
    ...
    $A += $c

    Das passt.

    Danke!


    Michael L.

    Montag, 12. September 2016 01:24
  •  

    > Das passt.

    Ja, aber... ;) Man sollte dazu wissen, das beim hinzufügen mit "+=" Powershell im Speicher jedesmal zuerst ein komplett neues Array mit allen Elementen des alten Arrays + dem jeweils neu hinzugefügten Element erzeugt und dann erst das alte Array überschreibt. Das liegt daran, das Arrays per Definition Elementgruppen mit fest definierter Größe sind.

    Diese Methode ist bei 5-10 Elementen egal, bei Hunderten und mehr, sehr, sehr langsam. Unter Umständen kann sogar der Speicher zum Problem werden.

    Lösung: Eine dynamische Liste statt dessen nehmen.

    $meinArray = New-Object System.Collections.Generic.List[System.Object]
    $meinArray.add($meinElement)  oder   
    $meinArray.add("Test")

     
    Neben einer nun sehr effizenten add()-Methode kannst du damit sogar Elemente wieder entfernen, mit remove().


    Grüße, Denniver


    Blog: http://bytecookie.wordpress.com

    Neu: Powershell Code Manager v5 ! Link
    (Schneller, besser + komfortabler scripten.)

    Hilf mit und markiere hilfreiche Beiträge mit dem "Abstimmen"-Button (links) und Beiträge die eine Frage von dir beantwortet haben, als "Antwort" (unten).
    Warum das Ganze? Hier gibts die Antwort.

    Montag, 12. September 2016 13:03
    Moderator
  • Hallo,

    zur Anmerkung von Denniver hier zwei Beispiele um die Laufzeit zu vergleichen.

    # Array
    Measure-Command {
    	$Array = @()
    	$i = 0
    	do {
    		$Array += "$i" 
    		$i++
    	} until ($i -gt 100000)
    }
    
    # ArrayList 
    Measure-Command {
    	$ArrayList = New-Object System.Collections.ArrayList
    	$i = 0
    	do {
    		$Null = $ArrayList.Add("$i")
    		$i++
    	} until ($i -gt 100000)
    }

    Beste Gruesse
    brima

    Montag, 12. September 2016 14:46
  • Ja super - ist schon beeindruckend. Hab mir mal die Mühe gemacht, das grafisch darzustellen.

    Dazu habe ich Excel und folgenden Code benutzt:

    $NumElements = 32000
    $ResultList = New-Object System.Collections.ArrayList
    
    # Array
    $i = 0
    $Array = @()
    do {
        $ResultElement = New-Object -TypeName Object
        Add-Member -InputObject $ResultElement -MemberType NoteProperty -Name Index -Value 0
        Add-Member -InputObject $ResultElement -MemberType NoteProperty -Name Statically -Value 0
        Add-Member -InputObject $ResultElement -MemberType NoteProperty -Name Dynamically -Value 0
        Add-Member -InputObject $ResultElement -MemberType NoteProperty -Name Factor -Value 0
        $s = (Measure-Command {$Array += "$i"}).TotalMilliseconds
        $ResultList.Add($ResultElement)
        $ResultElement.Index = $i
        $ResultElement.Statically = $s
        $i++
    } until ($i -gt $NumElements)
    
    # ArrayList 
    $i = 0
    $ArrayList = New-Object System.Collections.ArrayList
    do {
    	$d = (Measure-Command {$Null = $ArrayList.Add("$i")}).TotalMilliseconds
        $ResultList[$i].Dynamically = $d
        $ResultList[$i].Factor = $ResultList[$i].Statically / $ResultList[$i].Dynamically
        $i++
    } until ($i -gt $NumElements)
    $ResultList | Export-Csv -Path D:\Temp\ArrayMeasureCompare.csv -NoClobber -NoTypeInformation -Encoding ASCII -Delimiter ";"


    Michael L.

    Freitag, 16. September 2016 18:12