Przed PowerShell 3
Rozszerzalny system typów programu PowerShell pierwotnie nie pozwalał na tworzenie konkretnych typów, które można przetestować w stosunku do sposobu, w jaki zrobiłeś to w parametrze. Jeśli nie potrzebujesz tego testu, możesz zastosować dowolną z innych metod wymienionych powyżej.
Jeśli chcesz mieć rzeczywisty typ, do którego możesz rzutować lub sprawdzać typy, jak w przykładowym skrypcie ... nie można tego zrobić bez napisania go w C # lub VB.net i skompilowania. W PowerShell 2 możesz użyć polecenia „Add-Type”, aby zrobić to całkiem prosto:
add-type @"
public struct contact {
public string First;
public string Last;
public string Phone;
}
"@
Uwaga historyczna : w PowerShell 1 było jeszcze trudniej. Trzeba było ręcznie użyć CodeDom, na PoshCode.org jest bardzo staryskryptfunkcji nowej struktury, który pomoże. Twój przykład to:
New-Struct Contact @{
First=[string];
Last=[string];
Phone=[string];
}
Używanie Add-Type
lub New-Struct
pozwoli ci faktycznie przetestować klasę w twojej param([Contact]$contact)
i stworzyć nowe za pomocą $contact = new-object Contact
i tak dalej ...
W programie PowerShell 3
Jeśli nie potrzebujesz „prawdziwej” klasy, na którą możesz rzucić, nie musisz używać metody Add-Member, którą przedstawili powyżej Steven i inni .
Od PowerShell 2 można użyć parametru -Property dla New-Object:
$Contact = New-Object PSObject -Property @{ First=""; Last=""; Phone="" }
W PowerShell 3 mamy możliwość użycia PSCustomObject
akceleratora do dodania TypeName:
[PSCustomObject]@{
PSTypeName = "Contact"
First = $First
Last = $Last
Phone = $Phone
}
Nadal otrzymujesz tylko jeden obiekt, więc powinieneś utworzyć New-Contact
funkcję, aby upewnić się, że każdy obiekt wychodzi tak samo, ale teraz możesz łatwo sprawdzić, czy parametr „jest” jednym z tych typów, dekorując parametr PSTypeName
atrybutem:
function PrintContact
{
param( [PSTypeName("Contact")]$contact )
"Customer Name is " + $contact.First + " " + $contact.Last
"Customer Phone is " + $contact.Phone
}
W programie PowerShell 5
W PowerShell 5 wszystko się zmienia i w końcu otrzymaliśmy class
i enum
jako słowa kluczowe języka do definiowania typów (nie ma, struct
ale to jest w porządku):
class Contact
{
[ValidateNotNullOrEmpty()][string]$First
[ValidateNotNullOrEmpty()][string]$Last
[ValidateNotNullOrEmpty()][string]$Phone
Contact($First, $Last, $Phone) {
$this.First = $First
$this.Last = $Last
$this.Phone = $Phone
}
}
Mamy również nowy sposób tworzenia obiektów bez użycia New-Object
: [Contact]::new()
- tak naprawdę, gdybyś utrzymywał prostą klasę i nie definiował konstruktora, możesz tworzyć obiekty, rzucając tablicę haszy (chociaż bez konstruktora nie byłoby sposobu aby wymusić, że wszystkie właściwości muszą być ustawione):
class Contact
{
[ValidateNotNullOrEmpty()][string]$First
[ValidateNotNullOrEmpty()][string]$Last
[ValidateNotNullOrEmpty()][string]$Phone
}
$C = [Contact]@{
First = "Joel"
Last = "Bennett"
}
Add-Type
? Wydaje się, że działa w PowerShell 2 na Win 2008 R2. Powiedzieć zdefiniowaćcontact
używającAdd-Type
jak w swojej odpowiedzi, a następnie utworzyć instancję:$con = New-Object contact -Property @{ First="a"; Last="b"; Phone="c" }
. Potem dzwoni Funkcja ta działa:function x([contact]$c) { Write-Host ($c | Out-String) $c.GetType() }
, ale wywołanie tej funkcji nie powiedzie się,x([doesnotexist]$c) { Write-Host ($c | Out-String) $c.GetType() }
. Wywołaniex 'abc'
również kończy się niepowodzeniem i pojawia się odpowiedni komunikat o błędzie dotyczący przesyłania. Testowane na PS 2 i 4.Add-Type
@ jpmc26, powiedziałem, że nie możesz tego zrobić bez kompilacji (tj .: bez pisania w C # i wywoływaniaAdd-Type
). Oczywiście z PS3 można - jest[PSTypeName("...")]
atrybut, który pozwala określić typ jako string, który obsługuje testowanie z PSCustomObjects z ustawionym PSTypeNames ...Tworzenie niestandardowych typów można wykonać w programie PowerShell.
Kirk Munro ma w rzeczywistości dwa świetne posty, które szczegółowo opisują proces.
Książka Windows PowerShell w akcji autorstwa Manninga zawiera również przykład kodu do tworzenia języka specyficznego dla domeny w celu tworzenia niestandardowych typów. Książka jest doskonała pod każdym względem, więc naprawdę ją polecam.
Jeśli szukasz szybkiego sposobu na wykonanie powyższego, możesz utworzyć funkcję do tworzenia niestandardowego obiektu, takiego jak
function New-Person() { param ($FirstName, $LastName, $Phone) $person = new-object PSObject $person | add-member -type NoteProperty -Name First -Value $FirstName $person | add-member -type NoteProperty -Name Last -Value $LastName $person | add-member -type NoteProperty -Name Phone -Value $Phone return $person }
źródło
Oto metoda skrótów:
$myPerson = "" | Select-Object First,Last,Phone
źródło
$myPerson = 1 | Select First,Last,Phone
NoteProperty
odstring
typu, toProperty
bez względu na rodzaj przypisaniu w obiekcie. To jest szybkie i jednak spełnia swoje zadanie.Odpowiedź Stevena Murawskiego jest świetna, jednak podoba mi się krótszy (a raczej po prostu schludniejszy obiekt wyboru zamiast używania składni add-member):
function New-Person() { param ($FirstName, $LastName, $Phone) $person = new-object PSObject | select-object First, Last, Phone $person.First = $FirstName $person.Last = $LastName $person.Phone = $Phone return $person }
źródło
New-Object
nie jest nawet potrzebne. To zrobi to samo:... = 1 | select-object First, Last, Phone
int
sposobu: 1) działa szybciej, niewiele, ale dla tej konkretnej funkcjiNew-Person
różnica wynosi 20%; 2) najwyraźniej łatwiej jest wpisać. Jednocześnie, stosując to podejście praktycznie wszędzie, nigdy nie widziałem żadnych wad. Ale zgadzam się: mogą zdarzyć się rzadkie przypadki, gdy PSCustomObject jest trochę lepszy.Zaskoczony, nikt nie wspomniał o tej prostej opcji (w porównaniu z 3 lub nowszą wersją) do tworzenia obiektów niestandardowych:
[PSCustomObject]@{ First = $First Last = $Last Phone = $Phone }
Typem będzie PSCustomObject, a nie rzeczywisty typ niestandardowy. Ale to prawdopodobnie najłatwiejszy sposób na stworzenie niestandardowego obiektu.
źródło
Istnieje koncepcja PSObject i Add-Member, której możesz użyć.
$contact = New-Object PSObject $contact | Add-Member -memberType NoteProperty -name "First" -value "John" $contact | Add-Member -memberType NoteProperty -name "Last" -value "Doe" $contact | Add-Member -memberType NoteProperty -name "Phone" -value "123-4567"
To daje takie wyniki jak:
[8] » $contact First Last Phone ----- ---- ----- John Doe 123-4567
Inną alternatywą (o której wiem) jest zdefiniowanie typu w C # / VB.NET i załadowanie tego zestawu do programu PowerShell w celu bezpośredniego użycia.
Takie zachowanie jest zdecydowanie zalecane, ponieważ umożliwia innym skryptom lub sekcjom skryptu pracę z rzeczywistym obiektem.
źródło
Oto trudna ścieżka tworzenia niestandardowych typów i przechowywania ich w kolekcji.
$Collection = @() $Object = New-Object -TypeName PSObject $Object.PsObject.TypeNames.Add('MyCustomType.Contact.Detail') Add-Member -InputObject $Object -memberType NoteProperty -name "First" -value "John" Add-Member -InputObject $Object -memberType NoteProperty -name "Last" -value "Doe" Add-Member -InputObject $Object -memberType NoteProperty -name "Phone" -value "123-4567" $Collection += $Object $Object = New-Object -TypeName PSObject $Object.PsObject.TypeNames.Add('MyCustomType.Contact.Detail') Add-Member -InputObject $Object -memberType NoteProperty -name "First" -value "Jeanne" Add-Member -InputObject $Object -memberType NoteProperty -name "Last" -value "Doe" Add-Member -InputObject $Object -memberType NoteProperty -name "Phone" -value "765-4321" $Collection += $Object Write-Ouput -InputObject $Collection
źródło
Oto jeszcze jedna opcja, która wykorzystuje podobny pomysł do rozwiązania PSTypeName wspomnianego przez Jaykula (a zatem wymaga również PSv3 lub nowszego).
Przykład
Person.Types.ps1xml
:<?xml version="1.0" encoding="utf-8" ?> <Types> <Type> <Name>StackOverflow.Example.Person</Name> <Members> <ScriptMethod> <Name>Initialize</Name> <Script> Param ( [Parameter(Mandatory = $true)] [string]$GivenName , [Parameter(Mandatory = $true)] [string]$Surname ) $this | Add-Member -MemberType 'NoteProperty' -Name 'GivenName' -Value $GivenName $this | Add-Member -MemberType 'NoteProperty' -Name 'Surname' -Value $Surname </Script> </ScriptMethod> <ScriptMethod> <Name>SetGivenName</Name> <Script> Param ( [Parameter(Mandatory = $true)] [string]$GivenName ) $this | Add-Member -MemberType 'NoteProperty' -Name 'GivenName' -Value $GivenName -Force </Script> </ScriptMethod> <ScriptProperty> <Name>FullName</Name> <GetScriptBlock>'{0} {1}' -f $this.GivenName, $this.Surname</GetScriptBlock> </ScriptProperty> <!-- include properties under here if we don't want them to be visible by default <MemberSet> <Name>PSStandardMembers</Name> <Members> </Members> </MemberSet> --> </Members> </Type> </Types>
Update-TypeData -AppendPath .\Person.Types.ps1xml
$p = [PSCustomType]@{PSTypeName='StackOverflow.Example.Person'}
$p.Initialize('Anne', 'Droid')
$p | Format-Table -AutoSize
$p.SetGivenName('Dan')
$p | Format-Table -AutoSize
Wyjaśnienie
PS1XML
lubAdd-Member
są ograniczone doNoteProperty
,AliasProperty
,ScriptProperty
,CodeProperty
,ScriptMethod
, iCodeMethod
(lubPropertySet
/MemberSet
, choć te podlegają takim samym ograniczeniom). Wszystkie te właściwości są tylko do odczytu.ScriptMethod
możemy oszukać powyższe ograniczenie. Np. Możemy zdefiniować metodę (np.Initialize
), Która tworzy nowe właściwości, ustawiając dla nas ich wartości; w ten sposób upewniając się, że nasz obiekt ma wszystkie właściwości potrzebne do działania innych naszych skryptów.SetGivenName
.To podejście nie jest idealne dla wszystkich scenariuszy; ale jest przydatny do dodawania zachowań klasowych do typów niestandardowych / może być używany w połączeniu z innymi metodami wymienionymi w innych odpowiedziach. Np. W prawdziwym świecie prawdopodobnie zdefiniowałbym
FullName
właściwość tylko w PS1XML, a następnie użyłbym funkcji do stworzenia obiektu z wymaganymi wartościami, na przykład:Więcej informacji
Zapoznaj się z dokumentacją lub plikiem typu OOTB w
Get-Content $PSHome\types.ps1xml
poszukiwaniu inspiracji.# have something like this defined in my script so we only try to import the definition once. # the surrounding if statement may be useful if we're dot sourcing the script in an existing # session / running in ISE / something like that if (!(Get-TypeData 'StackOverflow.Example.Person')) { Update-TypeData '.\Person.Types.ps1xml' } # have a function to create my objects with all required parameters # creating them from the hash table means they're PROPERties; i.e. updatable without calling a # setter method (note: recall I said above that in this scenario I'd remove their definition # from the PS1XML) function New-SOPerson { [CmdletBinding()] [OutputType('StackOverflow.Example.Person')] Param ( [Parameter(Mandatory)] [string]$GivenName , [Parameter(Mandatory)] [string]$Surname ) ([PSCustomObject][Ordered]@{ PSTypeName = 'StackOverflow.Example.Person' GivenName = $GivenName Surname = $Surname }) } # then use my new function to generate the new object $p = New-SOPerson -GivenName 'Simon' -Surname 'Borg' # and thanks to the type magic... FullName exists :) Write-Information "$($p.FullName) was created successfully!" -InformationAction Continue
źródło