wtorek, 4 marca 2014

Listy, słowniki, tuple i domyślne wartości w Power Shell

Tworzenie generyków w PS jest tak ciężkie i nie przyjemne w pisaniu, że ludzie nie używają ich. Postanowiłem coś z tym zrobić i napisałem funkcje, które tworzą obiekty generyczne:
function New-Generic
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string]$type,

    [Parameter(Mandatory=$true, Position=1)] 
    [ValidateNotNullOrEmpty()]
    [string[]]$typeParameter,

    [Parameter(Mandatory=$false, Position=3)] 
    [array]$constructorParameters=$null
    )

    $genericType = [type]($type + ‘`’ + $typeParameter.Count)
    [type[]] $tParameters = $typeParameter
    $closedType = $genericType.MakeGenericType($tParameters)

    Write-Verbose "Creating object $closedType with parameters $constructorParameters" 
    return ,([Activator]::CreateInstance($closedType, [Object[]]$constructorParameters))
}
Najwięcej czasu w napisaniu tej funkcji zajęło mi dodanie przecinka po słowie return. Ponad jeden dzień spędziłem w szukaniu dlaczego kastuje mi do zwyczajnego obiektu. Ten przecinek robi jakąś magię :)

Powyższa funkcja tworzy obiekt, który jest generyczny. Postanowiłem owrappować tą funkcję z najczęściej używanymi kolekcjami generycznymi:
function New-List
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string] $typeParameter 
    )
   return ,( New-Generic -type 'System.Collections.Generic.List' -typeParameter $typeParameter )
}


function New-LinkedList
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string] $typeParameter 
    )
   return ,( New-Generic -type 'System.Collections.Generic.LinkedList' -typeParameter $typeParameter )
}

function New-Queue
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string] $typeParameter 
    )
   return ,( New-Generic -type 'System.Collections.Generic.Queue' -typeParameter $typeParameter )
}

function New-SortedSet
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string] $typeParameter 
    )
   return ,( New-Generic -type 'System.Collections.Generic.SortedSet' -typeParameter $typeParameter )
}

function New-Stack
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string] $typeParameter 
    )

   return ,( New-Generic -type 'System.Collections.Generic.Stack' -typeParameter $typeParameter )
}

function New-HashSet
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string] $typeParameter 
    )

   return ,( New-Generic -type 'System.Collections.Generic.HashSet' -typeParameter $typeParameter )
}

function New-Dictionary
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string]$keyType,

    [Parameter(Mandatory=$true, Position=1)] 
    [ValidateNotNullOrEmpty()]
    [string]$valueType
    )
    return ,(New-Generic -type 'System.Collections.Generic.Dictionary' -typeParameter $keyType,$valueType)
}

function New-SortedDictionary
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string]$keyType,

    [Parameter(Mandatory=$true, Position=1)] 
    [ValidateNotNullOrEmpty()]
    [string]$valueType
    )
    return ,(New-Generic -type 'System.Collections.Generic.SortedDictionary' -typeParameter $keyType,$valueType)
}

function New-SortedList
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string]$keyType,

    [Parameter(Mandatory=$true, Position=1)] 
    [ValidateNotNullOrEmpty()]
    [string]$valueType
    )
    return ,(New-Generic -type 'System.Collections.Generic.SortedList' -typeParameter $keyType,$valueType)
}

function New-KeyValuePair
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string]$keyType,

    [Parameter(Mandatory=$true, Position=1)] 
    [ValidateNotNullOrEmpty()]
    [string]$valueType
    )
    return ,(New-Generic -type 'System.Collections.Generic.KeyValuePair' -typeParameter $keyType,$valueType)
}
Przykład użycia dla listy jest poniżej. Można zauważyć rzucenie wyjątku kiedy będziemy chcieli dodać obiekt do listy, który nie może być kastowany do int:
$list = New-List "int" -Verbose
$list.GetType() 
$list.Add(1)
$list.Add('2')
$list.Add("three") #throw exception
Również można zainicjalizować stos doubli:
$stack= New-Stack -typeParameter 'double'
$stack.getType()
$stack.Push(1)
$stack.Push(20000)
$stack.Push(-1.123)
$stack.Pop()
A tworzenie słownika wygląda następująco:
$dic = New-Dictionary -keyType 'string' -valueType 'int'
$dic.Add("one",1)
$dic.Add("two",2)
Zanim stworzy się funkcję dla tupli to będzie potrzebna funkcja zwracająca domyślne wartości:
function Get-DefaultValue
{
    param(
    [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
    [type]$type
    )
    process{

        if($type.IsValueType)
        {
            return [Activator]::CreateInstance($type)
        }
        return $null
    }
}

Wynik dla boola:
Get-DefaultValue 'bool' #False
A funkcja do tworzenia tupli jest następująca:
function New-Tuple
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, Position=0)] 
    [ValidateNotNullOrEmpty()]
    [string[]] $typeParameter ,

    [Parameter(Mandatory=$false, Position=1)] 
    [array]$constructorParameters
    )

    if(-not($constructorParameters))
    {
        $constructorParameters = $typeParameter |Get-DefaultValue

    }
   return ,( New-Generic -type 'System.Tuple' -typeParameter $typeParameter -constructorParameters $constructorParameters )
}
Oczywiście można stworzyć tuple dla dowolnej (o ile pozwoli konstruktor) ilości parametrów
$tuple3= new-Tuple -typeParameter 'int', 'object', 'string'

$tuple3.GetType();
$tuple3.Item1

$tuple3= new-Tuple -typeParameter 'int', 'bool', 'string' -constructorParameters 1,$true, "SuperString"
$tuple3.Item2
$tuple3.Item3


Brak komentarzy:

Prześlij komentarz