none
Objekte typisieren mit .TypeNames.Insert und .GetType() in Powershell RRS feed

  • Frage

  • Mithilfe von .TypeNames.Insert soll es möglich sein, einem benutzerdefinierten Objekt - egal ob es mit der "Select" oder der New-Object-Methode erstellt wurde - einen Objekttyp zu geben.

    Es sollte dann auch möglich sein, diesen Objekttyp mit .GetType() wieder auszulesen.

    So wie in den existierenden Beispielen scheint das aber nicht (mehr ?) zu funktionieren:
    Es wird immer nur "PsObject" zurückgeliefert!

    http://technet.microsoft.com/en-us/magazine/hh750381.aspx
    http://stackoverflow.com/questions/12682631/change-the-type-name-of-a-powershell-object-from-pscustomobject-to-something-i-c


    Gibt es hier vielleicht Versionsunterschiede zwischen PowerShell 1.0 und 2.0?

    Ich habe einen Workaround gefunden, der aber sicher weniger performant ist, als
    .GetType().

    $o ="" | Select Bug, Fryingpan
    $o.PSobject.TypeNames.Insert(0,"Ladybug")
    "GetType: " + $o.GetType().Name
    # Workaround:
    "TypeName: " + ($o | Get-Member  | Select -First 1 ).TypeName

    Oder geht es doch noch irgendwie mit .GetType()?

    Mittwoch, 16. Januar 2013 09:00

Antworten

  • Warum willst Du den Objekten denn einen Namen geben? Geht es Dir eher um Kosmetik oder erwartest Du eine Funktion dahinter?

    Wenn es Dir um das Umwandeln von Objekten geht, ist der Name wie schon beschrieben nicht genug. Auch die System.Convert Klasse wird wahrscheinlich nicht ausreichen, wenn Du Objekte hast, die, was die Vererbung angeht, nicht voneinander abhängen.

    Wenn Du zwei Klassen hast, deren Objekte in die jeweilig andere Klasse gewandelt werden können sollen, geht dies über das Definieren impliziter Operatoren. Klingt kompliziert, ist es aber nicht:

    Type conversions with implicit and explicit operators

    Coversion operators

    Das ist dann aber weit weg vom "einfachen" PowerShell Scripting, kann aber in der PowerShell extrem hilfreich sein, wenn man eine Klasse so gebaut hat.


    -Raimund


    Mittwoch, 16. Januar 2013 13:09
  • Hallo Norbert,

    ab Powershell v5 kannst du Klassen benutzen, die dann auch mit GetType() den "korrekten" Typ zurück geben. 

    class MyObject
    {
     [string] $ComputerName;
     [string] $Model;
     [string] $Manufacturer;
     [string] $BIOSSerial;
     [string] $OSArchitecture;
     [string] $OSVersion;
     [string] $ProcArchitecture;
    }
    
    $os = Get-WmiObject –Class Win32_OperatingSystem –comp localhost
    $cs = Get-WmiObject –Class Win32_ComputerSystem –comp localhost
    $bios = Get-WmiObject –Class Win32_BIOS –comp localhost
    $proc = Get-WmiObject –Class Win32_Processor –comp localhost | Select-Object –First 1
    $props = @{OSVersion=$os.version
    Model=$cs.model
    Manufacturer=$cs.manufacturer
    BIOSSerial=$bios.serialnumber
    ComputerName=$os.CSName
    OSArchitecture=$os.osarchitecture
    ProcArchitecture=$proc.addresswidth}
    $obj =  New-Object –TypeName MyObject –Property $props
    Write-Output $obj
    Write-Output $obj.GetType().Name
    $obj | Format-Custom

    Oder hier mit Einbindung von C#-Code, wie du es bereits angesprochen hast.

    $source=@"
    public class MyObject
    {
    public string ComputerName {get; set;}
    public string Model {get; set;}
    public string Manufacturer {get; set;}
    public string BIOSSerial {get; set;}
    public string OSArchitecture {get; set;}
    public string OSVersion {get; set;}
    public string ProcArchitecture {get; set;}
    }
    "@
    Add-Type -TypeDefinition $source -Language CSharpversion3
    $os = Get-WmiObject –Class Win32_OperatingSystem –comp localhost
    $cs = Get-WmiObject –Class Win32_ComputerSystem –comp localhost
    $bios = Get-WmiObject –Class Win32_BIOS –comp localhost
    $proc = Get-WmiObject –Class Win32_Processor –comp localhost |Select –First 1
    $props = @{OSVersion=$os.version
    Model=$cs.model
    Manufacturer=$cs.manufacturer
    BIOSSerial=$bios.serialnumber
    ComputerName=$os.CSName
    OSArchitecture=$os.osarchitecture
    ProcArchitecture=$proc.addresswidth}
    $obj = New-Object –TypeName MyObject –Property $props
    Write-Output $obj
    Write-Output $obj.GetType().Name

    Das sollte doch genau das sein, was du benötigst?!

    Schau auch noch hier

    Wenn du schon entsprechende HashTables gebaut hast, könntest du diese sogar per Casting konvertieren.

    class MyObject
    {
     [string] $ComputerName;
     [string] $Model;
     [string] $Manufacturer;
     [string] $BIOSSerial;
     [string] $OSArchitecture;
     [string] $OSVersion;
     [string] $ProcArchitecture;
    }
    
    $os = Get-WmiObject –Class Win32_OperatingSystem –comp localhost
    $cs = Get-WmiObject –Class Win32_ComputerSystem –comp localhost
    $bios = Get-WmiObject –Class Win32_BIOS –comp localhost
    $proc = Get-WmiObject –Class Win32_Processor –comp localhost | Select-Object –First 1
    $props = @{OSVersion=$os.version
    Model=$cs.model
    Manufacturer=$cs.manufacturer
    BIOSSerial=$bios.serialnumber
    ComputerName=$os.CSName
    OSArchitecture=$os.osarchitecture
    ProcArchitecture=$proc.addresswidth}
    
    $test=[MyObject]$props
    $test.GetType()

    Alternativ ist Folgendes etwas schneller als dein damaliger WA:

    $obj.PsObject.Typenames[0]

    Liebe Grüße


    Best regards,

    David das Neves



    Please vote as helpful and mark as answer if a post helped you.
    This posting is provided "AS IS" with no warranties, and confers no rights.

    • Als Antwort markiert NLA Dienstag, 23. Februar 2016 16:08
    Mittwoch, 6. Januar 2016 18:28
  • Ich habe diese Vorgehensweise auch in meinem Blog beschrieben
    PowerShell eigene Objekte erstellen Custom Objects
    http://www.admin-source.de/BlogDeu/463/powershell-eigene-objekte-erstellen-custom-objects

    Man darf sich das nicht so einfach vorstellen!

    Wenn man an deiner Tür das Namensschild ändert, dann ändert sich ja nicht Bewohner!
    Das Ändern des Namens Ist nur für PowerShell sichtbar und dies wird NUR von einigen (nicht allen) PowerShell Cmdlets und Methoden beachtet!
    Die Methode xxx.getType() ist aber eine .NET Funktion!
    xxx.getType() schaut nicht auf das Namenschild an der Tür, sondern es schaut in den Raum hinein, welches Objekt sich darin befindet!

    Um den Objekt-Typen auf .NET Ebene zu ändern, musst du das Objekt mit .Net Funktionen oder CType() oder DirectCast() umwandeln (Fachwort: Casten oder Casting). In PowerShell geht das auch über die Type-Acceleratoren. Die Objekte sind danach wirklich einen anderer Typ!

    PowerShell Beispiel:

    # String in einen Integer wandeln
    ([Int]"12").GetType()
    
    # String eines Datums in ein DateTime Objekt verwandeln
    [DateTime]"01.01.2013" | Get-Member
    
    # jedes .NET Objekt (Klasse) das eine Parse() Methode hat, kann auf diese Weise in PowerShell gecastet werden
    ([Version]'1.1.1').gettype()
    


    Please click “Mark as Answer” if my post answers your question and click “Vote As Helpful” if my Post helps you.
    Bitte markiere hilfreiche Beiträge von mir als “Als Hilfreich bewerten” und Beiträge die deine Frage ganz oder teilweise beantwortet haben als “Als Antwort markieren”.
    My PowerShell Blog http://www.admin-source.info
    [string](0..21|%{[char][int]([int]("{0:d}" -f 0x28)+('755964655967-86965747271757624-8796158066061').substring(($_*2),2))})-replace' '
    German ? Come to German PowerShell Forum!

    Mittwoch, 16. Januar 2013 11:49

Alle Antworten

  • Ich habe diese Vorgehensweise auch in meinem Blog beschrieben
    PowerShell eigene Objekte erstellen Custom Objects
    http://www.admin-source.de/BlogDeu/463/powershell-eigene-objekte-erstellen-custom-objects

    Man darf sich das nicht so einfach vorstellen!

    Wenn man an deiner Tür das Namensschild ändert, dann ändert sich ja nicht Bewohner!
    Das Ändern des Namens Ist nur für PowerShell sichtbar und dies wird NUR von einigen (nicht allen) PowerShell Cmdlets und Methoden beachtet!
    Die Methode xxx.getType() ist aber eine .NET Funktion!
    xxx.getType() schaut nicht auf das Namenschild an der Tür, sondern es schaut in den Raum hinein, welches Objekt sich darin befindet!

    Um den Objekt-Typen auf .NET Ebene zu ändern, musst du das Objekt mit .Net Funktionen oder CType() oder DirectCast() umwandeln (Fachwort: Casten oder Casting). In PowerShell geht das auch über die Type-Acceleratoren. Die Objekte sind danach wirklich einen anderer Typ!

    PowerShell Beispiel:

    # String in einen Integer wandeln
    ([Int]"12").GetType()
    
    # String eines Datums in ein DateTime Objekt verwandeln
    [DateTime]"01.01.2013" | Get-Member
    
    # jedes .NET Objekt (Klasse) das eine Parse() Methode hat, kann auf diese Weise in PowerShell gecastet werden
    ([Version]'1.1.1').gettype()
    


    Please click “Mark as Answer” if my post answers your question and click “Vote As Helpful” if my Post helps you.
    Bitte markiere hilfreiche Beiträge von mir als “Als Hilfreich bewerten” und Beiträge die deine Frage ganz oder teilweise beantwortet haben als “Als Antwort markieren”.
    My PowerShell Blog http://www.admin-source.info
    [string](0..21|%{[char][int]([int]("{0:d}" -f 0x28)+('755964655967-86965747271757624-8796158066061').substring(($_*2),2))})-replace' '
    German ? Come to German PowerShell Forum!

    Mittwoch, 16. Januar 2013 11:49
  • Warum willst Du den Objekten denn einen Namen geben? Geht es Dir eher um Kosmetik oder erwartest Du eine Funktion dahinter?

    Wenn es Dir um das Umwandeln von Objekten geht, ist der Name wie schon beschrieben nicht genug. Auch die System.Convert Klasse wird wahrscheinlich nicht ausreichen, wenn Du Objekte hast, die, was die Vererbung angeht, nicht voneinander abhängen.

    Wenn Du zwei Klassen hast, deren Objekte in die jeweilig andere Klasse gewandelt werden können sollen, geht dies über das Definieren impliziter Operatoren. Klingt kompliziert, ist es aber nicht:

    Type conversions with implicit and explicit operators

    Coversion operators

    Das ist dann aber weit weg vom "einfachen" PowerShell Scripting, kann aber in der PowerShell extrem hilfreich sein, wenn man eine Klasse so gebaut hat.


    -Raimund


    Mittwoch, 16. Januar 2013 13:09
  • Ich möchte hier meinen alten Thread noch einmal aufgreifen, da seinerzeit nicht klar geworden war, um was es mir eigentlich ging. Mein Lösungsansatz hat funktioniert, aber ganz befriedigend ist das nicht. Wenn ich einen "harten" benutzerdefinierten Datentyp will, muss ich C#-Code einbetten. 

    Ich möchte ein benutzerdefiniertes Objekt erstellen und diesem eine Typenbezeichnung geben, so wie Rückgabewerte von Commandlets diesen auch liefern.  Vom Ansatz her sind dynamische Objekte in PowerShell etwas anderes, als Instanzen von Klassen in C#, vielleicht sogar eher so wie in JavaScript.

    Auf jeden Fall können jederzeit Member hinzugefügt oder auch per Select-Object weggefiltert werden.

    Wenn ein Objekt nun aber eine bestimmte Typenbezeichnung hat, kann ich davon ausgehen, dass es wahrscheinlich auch bestimmte Member hat. Was für ein Objekt ich vor mir habe, muss ich dann nicht anhand der Kombination von Properties erraten, sondern habe wenigstens eine Grundannahme, was mich erwartet. Ich muss ggf. immer noch prüfen, ob ein Member tatsächlich existiert, aber die Unsicherheit ist etwas begrenzter.
    Das sehe ich nicht als Kosmetik.

    Ich erhalte damit die Möglichkeit, in einer Collection verschiedene Objekttypen zu haben. Damit lassen sich z.B. die verschiedenen Satzarten aus einer VSAM-Datei (Host ..Cobol..Steinzeit) hervorragend abbilden, oder auch Entitäten aus XML-Dateien, die zwar variabel sind, aber ggf. einem Schema folgen usw.

    Es gab auch mal einen Ansatz für dynamische Objekte in C#: https://clay.codeplex.com/
    Die Clay-Objekte hatten zwar keinen aussagekräftigen Typ, aber einen ShapeName.
    http://stackoverflow.com/questions/5075649/dump-objects-build-with-clay-in-c-sharp

    Wie handhabt ihr das inzwischen?


    NLA


    • Bearbeitet NLA Mittwoch, 6. Januar 2016 15:45
    Mittwoch, 6. Januar 2016 15:44
  • Hallo Norbert,

    ab Powershell v5 kannst du Klassen benutzen, die dann auch mit GetType() den "korrekten" Typ zurück geben. 

    class MyObject
    {
     [string] $ComputerName;
     [string] $Model;
     [string] $Manufacturer;
     [string] $BIOSSerial;
     [string] $OSArchitecture;
     [string] $OSVersion;
     [string] $ProcArchitecture;
    }
    
    $os = Get-WmiObject –Class Win32_OperatingSystem –comp localhost
    $cs = Get-WmiObject –Class Win32_ComputerSystem –comp localhost
    $bios = Get-WmiObject –Class Win32_BIOS –comp localhost
    $proc = Get-WmiObject –Class Win32_Processor –comp localhost | Select-Object –First 1
    $props = @{OSVersion=$os.version
    Model=$cs.model
    Manufacturer=$cs.manufacturer
    BIOSSerial=$bios.serialnumber
    ComputerName=$os.CSName
    OSArchitecture=$os.osarchitecture
    ProcArchitecture=$proc.addresswidth}
    $obj =  New-Object –TypeName MyObject –Property $props
    Write-Output $obj
    Write-Output $obj.GetType().Name
    $obj | Format-Custom

    Oder hier mit Einbindung von C#-Code, wie du es bereits angesprochen hast.

    $source=@"
    public class MyObject
    {
    public string ComputerName {get; set;}
    public string Model {get; set;}
    public string Manufacturer {get; set;}
    public string BIOSSerial {get; set;}
    public string OSArchitecture {get; set;}
    public string OSVersion {get; set;}
    public string ProcArchitecture {get; set;}
    }
    "@
    Add-Type -TypeDefinition $source -Language CSharpversion3
    $os = Get-WmiObject –Class Win32_OperatingSystem –comp localhost
    $cs = Get-WmiObject –Class Win32_ComputerSystem –comp localhost
    $bios = Get-WmiObject –Class Win32_BIOS –comp localhost
    $proc = Get-WmiObject –Class Win32_Processor –comp localhost |Select –First 1
    $props = @{OSVersion=$os.version
    Model=$cs.model
    Manufacturer=$cs.manufacturer
    BIOSSerial=$bios.serialnumber
    ComputerName=$os.CSName
    OSArchitecture=$os.osarchitecture
    ProcArchitecture=$proc.addresswidth}
    $obj = New-Object –TypeName MyObject –Property $props
    Write-Output $obj
    Write-Output $obj.GetType().Name

    Das sollte doch genau das sein, was du benötigst?!

    Schau auch noch hier

    Wenn du schon entsprechende HashTables gebaut hast, könntest du diese sogar per Casting konvertieren.

    class MyObject
    {
     [string] $ComputerName;
     [string] $Model;
     [string] $Manufacturer;
     [string] $BIOSSerial;
     [string] $OSArchitecture;
     [string] $OSVersion;
     [string] $ProcArchitecture;
    }
    
    $os = Get-WmiObject –Class Win32_OperatingSystem –comp localhost
    $cs = Get-WmiObject –Class Win32_ComputerSystem –comp localhost
    $bios = Get-WmiObject –Class Win32_BIOS –comp localhost
    $proc = Get-WmiObject –Class Win32_Processor –comp localhost | Select-Object –First 1
    $props = @{OSVersion=$os.version
    Model=$cs.model
    Manufacturer=$cs.manufacturer
    BIOSSerial=$bios.serialnumber
    ComputerName=$os.CSName
    OSArchitecture=$os.osarchitecture
    ProcArchitecture=$proc.addresswidth}
    
    $test=[MyObject]$props
    $test.GetType()

    Alternativ ist Folgendes etwas schneller als dein damaliger WA:

    $obj.PsObject.Typenames[0]

    Liebe Grüße


    Best regards,

    David das Neves



    Please vote as helpful and mark as answer if a post helped you.
    This posting is provided "AS IS" with no warranties, and confers no rights.

    • Als Antwort markiert NLA Dienstag, 23. Februar 2016 16:08
    Mittwoch, 6. Januar 2016 18:28