Get-TypeName

# 緒言

[*Effective Windows PowerShell: The Free eBook*](https://rkeithhill.wordpress.com/2009/03/08/effective-windows-powershell-the-free-ebook/ "Effective Windows PowerShell: The Free eBook | Keith Hill's Blog") は PowerShell 2.0 までをあつかっていますが、いまでもおそわることはおおくあるとおもいます。公式のドキュメントではきづかなかったことがたくさんかかれています。

その `Effective Windows PowerShell.pdf` の **Item 3: Know What Objects Are Flowing Down the Pipeline**[^1] に

[^1]: もとになったブログは [*Effective PowerShell Item 6: Know What Objects Are Flowing Down the Pipe*](https://rkeithhill.wordpress.com/2007/09/04/effective-powershell-item-6-know-what-objects-are-flowing-down-the-pipe/ "Effective PowerShell Item 6: Know What Objects Are Flowing Down the Pipe | Keith Hill' Blog")

> ```powershell:PowerShell
filter Get-TypeName {if ($_ -eq $null) {'<null>'} else {$_.GetType().Fullname }}
```

は `$profile` にいれておきたいくらい便利とあります。ただし、

> ```
PS> @() | Get-TypeName
```

とするとなにも出力されません。配列はパイプにおくられるときに要素に展開されますが、ここで `@()` の要素はからなのでパイプにはなにもおくられないということでしょう。

[The PowerShell Community Extensions](https://github.com/Pscx/Pscx "GitHub - Pscx/Pscx: PowerShell Community Extensions module repository") (PSCX) の `Get-TypeName` をつかうと、このようなばあいに警告を表示してくれるとのことです。

しかし、さしあたり `Get-TypeName` のみがほしいのと、これを PS の `function` で実装できないかしらとおもったのでした[^2]。

[^2]: PS 2.0 まででは不可能だったのかもしれません。

# 移植

PSCX の `Get-TypeName` の C♯ の [ソース コード](https://github.com/Pscx/Pscx/blob/master/Src/Pscx/Commands/GetTypeNameCommand.cs "Pscx/GetTypeNameCommand.cs at master · Pscx/Pscx · GitHub") を PS におきかえただけです。

```powershell
function Get-TypeName {
    [CmdletBinding()]
    [OutputType([String], [PSObject])]
    param(
        [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
        [AllowNull()]
        [PSObject]$InputObject,
        [Parameter(Mandatory=$false)]
        [Switch]$FullName,
        [Parameter(Mandatory=$false)]
        [Switch]$PassThru
    )

    begin {
        $_processedAnyInput = $false
    }

    process {
        if ($null -eq $InputObject) {
            Write-Host -Object '<null>'
        } else {
            $name = if ($FullName) {
                    $InputObject.GetType().FullName
                } else {
                    $InputObject.GetType().Name
                }
            if ($PassThru) {
                Write-Host -Object $name
                Write-Output -InputObject $InputObject
            } else {
                Write-Output -InputObject $name
            }
        }
        $_processedAnyInput = $true
    }

    end {
        if (-not $_processedAnyInput) {
            Write-Warning -Message (
                'Get-TypeName did not receive any input. The input may be an empty ' +
                'collection. You can either prepend the collection expression with ' +
                'the comma operator e.g. ",$collection | gtn" or you can pass the ' +
                'variable or expression to Get-TypeName as an argument e.g. ' +
                '"gtn $collection".'
            )
        }
    }
}

Set-Alias gtn Get-TypeName -Description "PSCX alias"
```

なお、オリジナルのコードできになるのは

> ```csharp:C#
                string name = InputObject.TypeNames[0];
                if (!FullName)
                {
                    if (name.Contains("`"))
                    {
                        string nonGenericPart = name.Split('[')[0];
                        int ndx = nonGenericPart.LastIndexOf('.');
                        if ((ndx >= 0) && (ndx < (nonGenericPart.Length - 1)))
                        {
                            name = nonGenericPart.Substring(ndx + 1);
                        }
                    }
                    else
                    {
                        // ...
                    }
                }
```

ですが、この部分は、たとえば

```
PS> ([System.Collections.Generic.List[int]]::new()).GetType().FullName
System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
PS> ([System.Collections.Generic.List[int]]::new()).GetType().Name
List`1
```

というふうになるので、 `GetType().Name` でまきとれているとおもいます。 

# 2020/07/22 追記

```powershell
    process {
        if ($InputObject -eq $null) {
    # ...
```

としていましたが、 [Everything you wanted to know about arrays | $null or empty](https://docs.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-arrays?view=powershell-5.1#null-or-empty "Everything you wanted to know about arrays - PowerShell | Microsoft Docs") にしたがい

```powershell
    process {
        if ($null -eq $InputObject) {
    # ...
```

に修正しました。

ただ、当該記事で

> ```powershell
$array = @('one',$null,'three')
if ( $array -eq $null)
{
    'I think Array is $null, but I would be wrong'
}
```

はバージョン 5.1.19041.1 では著者の意図どおりにはなりませんでした。たしかに、

```console
PS> Get-Member -InputObject ($array -eq $null)


   TypeName: System.Object[]
...
```

となるので、 ` $array -eq $null ` が ` $null ` が要素の要素数 1 の配列つまり ` $true ` 相当と評価されてしかるべきとおもいますが…。