💪

PowerShellで拡張メソッドを書きたかったけど、それってValueFromPipelineで済むのでは?と気づいてのアレコレ。

2023/06/14に公開

発端

C#に慣れ親しんでいるPowerShell使いは、稀に拡張メソッドを生やしたくなる。

先駆者様
https://stuncloud.wordpress.com/2021/03/11/scriptmethod/

しかし、最近PowerShellでデータ加工するモジュールを作っていて気づいた。

これ、ValueFromPipelineで済むのでは?

と思っていろいろ試してみた。

さくっと

例えば文字列に関する拡張メソッドを生やしたいとき、下記のような関数を作ればそれっぽいことができる。

function ReplaceAtoa {
    param (
        [Parameter(ValueFromPipeline)][string]$text
    )
    return $text -replace "A","a"
}
"GAFA, GAFA !" | ReplaceAtoa

List[int]に生やしたい時、直感的に書くと下記になりそう。

function PlusOne {
    param (
        [Parameter(ValueFromPipeline)][System.Collections.Generic.List[int]]$_input
    )

    return $_input | % { $_ + 1 }
}
(1..5) | PlusOne

だが、これは思った挙動にならない。
PowerShell的なコードはこんな感じ↓(他にも最適なコードがあるはず)。

function PlusOne {
    param (
        [Parameter(ValueFromPipeline)][int]$_input
    )

    process{
        return $_ + 1
    }
}

[System.Collections.Generic.List[int]](1..5) | PlusOne

じゃあフィルタ(Whereみたいなやつ)が作りたいときはどうするのか。
不要なreturnをしなければいい。

function WhereEven {
    param (
        [Parameter(ValueFromPipeline)][int]$_input
    )

    process{
        if ($_input % 2 -eq 0) { return $_ };
    }
}

[System.Collections.Generic.List[int]](1..5) | WhereEven

終わりに

ValueFromPipelineでやりたかったことは代替できそう。
ただ、独自のインターフェース型を定義して~みたいな使い方は、PowerShell自体ではinterfaceの定義ができないので現状はC#で迂回策をとったりする必要がある。
まぁValueFromPipelineByPropertyNameでそれっぽいバリデーションチックなことができるけれど、個人的には[Parameter(ValueFromPipeline)][IPerson]$personみたいな感じで書きたいよねという気持ちはあるのでいつかインターフェースの定義ができるようになって欲しい。

質問とか、他にも有益そうな知見があればコメントください。

Discussion