TypeHelper.psm1

<#
 .Synopsis
 Helper functions for dealing with types
#>


Set-StrictMode -Version Latest

<#
 .Synopsis
 Find a specific type with all loaded assemblies
#>

function Find-Type
{
    [CmdletBinding()]
    param([Parameter(ParameterSetName="SearchByName")][String]$Typename,
          [Parameter(ParameterSetName="SearchByType", ValueFromPipeline=$true)][Type]$Type,
          [Switch]$IncludeInterface
          )
    $PropHash = @()
    $PropHash += @{n="Name";e={$_.Name}}
    $PropHash += @{n="Namespace";e={$_.Name}}
    if ($PSBoundParameters.ContainsKey("IncludeInterface"))
    {
        $PropHash += @{n="Interfaces";e={$_.GetInterfaces().Name -join ","}}
    }
    if ($PSBoundParameters.ContainsKey("Typename"))
    {
        # go through all loaded assemblies public types in classes only
        try {
            [System.AppDomain]::CurrentDomain.GetAssemblies().GetTypes().Where{
                $_.IsPublic -and $_.IsClass -and $_.Name -eq $Typename
            } | Select-Object $PropHash
        }
        catch
        {
            Write-Warning "Find-Type with Typename parameter: $_"    
        }
    }
    if ($PSBoundParameters.ContainsKey("Type"))
    {
        # go through all loaded assemblies public types in classes only
        try {
            [System.AppDomain]::CurrentDomain.GetAssemblies().GetTypes().Where{
                $_.FullName -eq $Type.FullName
            } | Select-Object $PropHash
        }
        catch
        {
            Write-Warning "Find-Type with Type parameter: $_"    
        }
    }
}

<#
 .Synopsis
 Finds all types that implements a specific interface
#>

function Find-Interface
{
    [CmdletBinding()]
    param([String]$Interfacename)
    $PropHash = @()
    $PropHash += @{n="Name";e={$_.Name}}
    $PropHash += @{n="Namespace";e={$_.Namespace}}
    # go through all loaded assemblies public types in classes only
    try {
        [System.AppDomain]::CurrentDomain.GetAssemblies().GetTypes().Where{
            $_.IsPublic -and $_.IsClass -and $_.GetInterfaces().Count -gt 0 -and
            $_.GetInterfaces().Name -eq $Interfacename
        } | Select-Object $PropHash | Sort-Object Name
    }
    catch
    {
        Write-Warning "Find-Interface: $_"    
    }
}

<#
 .Synopsis
 Get the constructors of a type
#>

function Get-Constructor
{
    param([Parameter(ValueFromPipeline=$true)][Type]$Type)

    $Type.GetConstructors().ForEach{
        [PSCustomObject]@{
            Name = $_.Name
            ParameterCount = $_.GetParameters().Count
            Parameters = $_.GetParameters().ForEach{
                "{0}:{1}" -f $_.Name, $_.ParameterType
            }
        }
    }
}

<#
.Synopsis
Finds all methods with a given name
#>

function Find-MethodMember
{
    [CmdletBinding()]
    param([String]$Methodname, [Switch]$FindExact)
    [AppDomain]::CurrentDomain.GetAssemblies().GetTypes().Where{
        $_.IsClass -and $_.IsPublic
    } | Where-Object {
        $Method = ""
        if ($PSBoundParameters.ContainsKey("FindExact"))
        {
            if ($_.GetMethods().Count -gt 0 -and $_.GetMethods().Name -eq $Methodname)
            {
                $Method = @($Methodname)
            }
        }
        else
        {
            if ($_.GetMethods().Count -gt 0 -and $_.GetMethods().Name -match $Methodname)
            {
                $Method = @($_.GetMethods().Name -match $Methodname)
            }
        }
        # Check if the potential array contains the method name
        # ignore the rule violation IncorrectComparison with null because its stupid (in this specific case)
        $Method -ne ""
    } | Select-Object -Property @{n="Method";e={$Method}},
                                @{n="Class";e={$_.Name}}, FullName, Module | Sort-Object -Property Method
}

<#
.Synopsis
Finds all properties with a given name
#>

function Find-PropertyMember
{
    [CmdletBinding()]
    param([String]$Propertyname, [Switch]$FindExact)
    [AppDomain]::CurrentDomain.GetAssemblies().GetTypes().Where{
        $_.IsClass -and $_.IsPublic
    } | Where-Object {
        $Property = ""
        if ($PSBoundParameters.ContainsKey("FindExact"))
        {
            if ($_.GetProperties().Count -gt 0 -and $_.GetProperties().Name -eq $Propertyname)
            {
                $Property =  $Propertyname 
            }
        }
        else
        {
            if ($_.GetProperties().Count -gt 0 -and $_.GetProperties().Name -match $Propertyname)
            {
                $Property = $Propertyname
            }
        }
        # Check if the potential array contains the property name
        # ignore the rule violation IncorrectComparison with null because its stupid (in this specific case)
        $Property -ne ""
    } | Select-Object -Property @{n="Property";e={$Property}},
                                @{n="Class";e={$_.Name}}, FullName, Module | Sort-Object -Property Property
}

<#
.Synopsis
Invoke a private generic method
.Notes
Requires that there is only one method with that name
#>

function Invoke-PrivateGenericMethod
{
    [CmdletBinding()]
    param([Type]$Type, [String]$Methodname, [Type]$ParamType, [Object]$ParamValue)
    # Call the method with a little bit of indirect reflection magic
    # Amazing how PowerShell simplifies the use of enum params
    # But beware: Works only with there is method overloading - otherwise parametertypes matters
    $Method = $Type.GetMethod($Methodname, @("Static","NonPublic"))
    # Get the generic version of that method
    $GenMethod = $Method.MakeGenericMethod($ParamType) 
    # Invoke it
    $GenMethod.Invoke($null, $ParamValue) 
}

<#
.Synopsis
Invoke a generic method with arguments
.Notes
Requires that the method parameters are non generic :(
#>


function Invoke-GenericMethodEx
{
    param([Type]$InstanceType, [Type]$GenericType, [String]$Methodname, [Object[]]$MethodParameters)
    $ParameterTypes = @($MethodParameters.ForEach{$_.GetType() })
    # Assuming there is only one method with that name
    $m = @($InstanceType.GetMethods().Where{$_.Name -eq $Methodname})
    if ($m.Length -gt 1)
    {
        throw "Multiple methods with $MethodName name found - try Invoke-GenericMethod"
    }
    # Convert every argument into a generic argument
    $GenParameters = $ParameterTypes.ForEach{ $m.GetGenericArguments()}
    # BindingFlags can be simplified in this method overloading
    # $Method = $InstanceType.GetMethod($MethodName, "Static,Public", $null, $ParameterTypes, $null)
    $Method = $InstanceType.GetMethod($MethodName, "Static,Public", $null, $GenParameters, $null)
    if ($Method -ne $null)
    {
        $GenMethod = $Method.MakeGenericMethod($GenericType)
        $GenMethod.Invoke($InstanceType, $MethodParameters)
   }
   else
   {
       throw "Method $MethodName not found on $InstanceType"
   }
}

<#
.Synopsis
Calls a generic method with only one overload and no generic arguments
#>

function Invoke-GenericMethod
{
    param([Type]$InstanceType, [Type]$GenericType, [String]$Methodname, [Object[]]$MethodParameters)
    $ParameterTypes = @($MethodParameters.ForEach{$_.GetType() })
    # Assuming there is only one method with that name
    $m = @($InstanceType.GetMethods().Where{$_.Name -eq $Methodname})
    # BindingFlags can be simplified in this method overloading
    $Method = $InstanceType.GetMethod($MethodName, "Static,Public", $null, $ParameterTypes, $null)
    if ($Method -ne $null)
    {
        $GenMethod = $Method.MakeGenericMethod($GenericType)
        $GenMethod.Invoke($InstanceType, $MethodParameters)
   }
   else
   {
       throw "Method $MethodName not found on $InstanceType"
   }
}