質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.58%

PowerShellで 別のファイルに定義されたクラス を 使いやすくしたい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 3,957

score 278

ベストアンサー後

頂いたコードを参考に
書き直してみました。

既存のインテリセンスに追加 したかったのですが
TabExpansion では方法が分からなかったため TabExpansion2を書き換えました。

定義した全てのクラス(Enum)が見たいときは `['まで打って Ctrl + Space で見られます。 なぜか ISEのスクリプトウィンドウ内だと 表示されませんが コンソールだと表示されます。 一先ずここで妥協します。

psm1 で保存し、ISEでの利用時には F8 等で 明示的に そのスクリプトを実行する必要があります。

  文字数超過で載せられませんでした
  下記記載の「インテリセンス 追加コード」へお願い致します。

インテリセンス 追加コード

途中経過 2019.03.06時点

一先ずpublicなクラスを継承して NameSpace を利用する 手段は見つけました。 一度実行すればタイプ指定時のインテリセンスも使えました。

Using Module .\class.psm1
[System.Reflection.AssemblyName] $AssemblyName = "assemblyBuilder"

$appDomain = [AppDomain]::CurrentDomain
$assemblyBuilder = $appDomain.DefineDynamicAssembly(
        $AssemblyName, 
        [System.Reflection.Emit.AssemblyBuilderAccess]::Run
    )
$moduleBuilder = $assemblyBuilder.DefineDynamicModule($AssemblyName.Name)

$TypeBuilder = $moduleBuilder.DefineType("kmkz.Form", [System.Reflection.TypeAttributes]::Public, [System.Windows.Forms.Form])

$TypeBuilder.CreateType() > $null

$TypeBuilder.Assembly.GetTypes().NameSpace | sort -Unique | foreach { Write-Host "ネームスペース : $_" }
Write-Host "フルネーム : $($Form.GetType().FullName)"

$Form = [kmkz.Form]::new()
$Form.ShowDialog() > $null

ですがメソッド等の追加の仕方も分からないですし... 自分で作ったクラスも継承できません [System.Reflection.TypeAttributes]::Public, [System.Windows.Forms.Form])[System.Windows.Forms.Form]を自分で定義したクラスにすると

"3" 個の引数を指定して "DefineType" を呼び出し中に例外が発生しました: "回収不可能アセンブリから回収可能アセンブリを参照することはできません。"

っとエラーになります..... 回収不可能アセンブリ も 回収可能アセンブリ も調べても何のことだか全くわかりません。

もし自作のクラスが継承できればそれで 一先ず目標は達成できるのですが...

そもそも ダイナミックモジュールじゃなくて普通のモジュールでは出来ないのでしょうか....?

前提・実現したいこと

ざっくりしたタイトルですみません。

叶えたいこと主には2つです。

  1. クラス利用時 インテリセンスの利用が可能
  2. クラスのグループ分け(NameSpace?)

私がPowerShellで初めてクラスを触ったときの質問で 別のファイルにクラスを定義するとそのままだと インテリセンスを使うのは絶望的 だと思い そこからずっと 定義したクラスを変数に格納して それを利用してきました。 そうすることでF8で実行してあげればインテリセンスが利用できるようになったからです。 そうしてずっと逃げてきたのです。

そんな中 とある質問でのZuishin様からの回答をきっかけに クラスの継承を 使うようになりました。 ですが、自分で定義したクラスをさらに継承(多重継承?)しようとした場合 今まで使っていた 変数に格納したクラスでは 継承が出来ず 再びインテリセンスが使えない状態に陥ってしまいました。 「どうせインテリセンスが使えないのなら全て諦めてしまおうと」そう思ったのですが、

Formに関する質問をしたときにimihito様に頂いたコードの中でモジュールなるものが使われていて 見よう見まねで書き変えて実行したとき 何かの要因でたまたま上手く実行できず 追加の質問をしたのですが その中で Imihito様

「PowerShell v5はあまり使ったことがありませんが、using module モジュール名でモジュール内のクラスのインテリセンスは効きそうですね」

っと云っていたのを思い出しました。 その時「試してみます」っと自分で云っていたのですが試してなかったので試してみました。

<#
    ファイル名 : Class.psm1
    の内容
#>
Class te2st
{
    PutText()
    {
        Write-host "methodが実行されました。"
    }
}
<#
    ファイル名 : Class.ps1
    の内容
#>
Using Module .\Class.psm1
# Import-Module -Name .\Class.psm1 <Using Module だけで動作したため不要>

$test = [te2st]::new()

結果は良好 staticメンバ も instanceメンバ もしっかりインテリセンスが使えました。 これで殆ど 目的は達成できているのですが [te2st]の入力時にインテリセンスが使えないので 使えるようにしたいのと

Enumの例なのではclassでは可能か分からない、 一度実行しないとインテリセンスが使えない、 C#をAdd-Typeで読み込むのと一緒で ISEだと 一度読み込んでしまうと 書き換えても再起動しないと 更新できないのが難点ですが このサイト[MyClass.te2st]のMyClassのように自分でNameSpace? を作ることが不可能ではない っということを知ってしまったので クラスのグループ分けが出来れば[MyClass.まで打ってインテリセンスを見たりすれば自分で定義したクラスが一目瞭然で しかも、もしも内容が異なる同じ名前のクラスを同時に使いたくなってもNameSpace? で分けてあれば悩む必要もなくなるだろう

っということで[MyClass.te2st]のようにグループ分けが出来るようにしたいです。

自分でネットなどでいろいろ情報を集めているのですが、このままだと
いつになったら出来るのか、そもそも出来るのかすら怪しいです。

代替策、そもそも論 等でも助かります。

宜しくお願い致します。

発生している問題

前提 : クラスは再利用が前提なので別ファイルになることは避けられなさそう

  1. クラスのインテリセンスが使えない (Type指定時)
    (ファイル名とクラス名が違ったり 一つのファイルに複数のクラスが定義されていると
    そのファイルを開いてクラス名を確認しないといけない)

補足情報(FW/ツールのバージョンなど)

Win10
powerShell_ISE Ver V5

どのパソコンで作業、実行 するのか決まっていないのと
毎回全てのクラスを使うわけではない(一つのプロジェクトのみでしか利用しないのもある)ので
プロファイル等 設定済みPCでしか利用できない方法は使えません。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • kamikazelight

    2019/03/08 16:54 編集

    出来ましたよ?
    使われていました

    キャンセル

  • Zuishin

    2019/03/08 17:00

    そうでしたか。では私の勘違いでしょう。
    TabExpansion2 の方が使いやすくていいですね。

    キャンセル

  • kamikazelight

    2019/03/08 17:06

    でも TabExpansion2 は戻り値が[String]ではなかったので
    苦労しましたw
    最初に TabExpansion を教えて頂けて助かりました。

    キャンセル

回答 1

checkベストアンサー

+3

名前空間の件ですが、class 構文で名前空間を指定する方法は見つかりませんでした。Add-Type ならありますが C# になります。
New-Enum はリフレクションで名前空間を実現しています。

補完の件ですが、ISE のエディタでも TabExpansion が使われていることを確認しました。function:\TabExpansion を書き換えれば補完の挙動を変えられますので、それでクラス名を補完するのが現実的だと思います。

現在使用可能なクラスは [AppDomain]::CurrentDomain.GetAssemblies()|%{$_.GetTypes()} で得ることができますが、補完のたびにこれを呼び出すのもパフォーマンスが落ちるでしょう。例えば指定したキーワードがコマンドラインにあった場合に読み直すようにするのはどうでしょうか?

追記

以下のスクリプトを実行することで、外部モジュールで作られたクラスが補完できることを確認しました。
なお、新クラスのアセンブリ名が PowerShell Class Assembly となるのは PowerShell Core の場合で、それ以前のバージョンの場合は /powershell のようでした。

function TabExpansion
{
    param($line, $lastWord)

    $lastBlock = [regex]::Split($line, '[|;]')[-1].TrimStart()
    $matchBackup = $Matches
    try {
        if ($lastBlock -match '(^\[)(.*)') {
            [AppDomain]::CurrentDomain.GetAssemblies() | Foreach-Object {
                $_.GetTypes()
            } | Where-Object {
                ($_.Name -notlike '*_<staticHelpers>') -and ($_.FullName -ilike "*$($Matches[2])*")
            } | ForEach-Object {
                '[' + $_.FullName + ']'
            }
        }
    } finally {
        $Matches = $matchBackup
    }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/06 18:39

    なお、class 構文で作られたクラスは次のように取得できることがわかりました。

    [AppDomain]::CurrentDomain.GetAssemblies()|?{$_.GetName().Name -eq 'PowerShell Class Assembly'}|%{$_.GetTypes()}

    これなら補完のたびに読み込んでもパフォーマンスは許容範囲に収まるかもしれません。

    キャンセル

  • 2019/03/06 21:20

    有難うございます。
    確認してみます。

    キャンセル

  • 2019/03/07 00:43

    再度確認してみたところ、うまく補完できませんでした。
    そこで Import-Module でモジュールを読み込んだところ、補完できました。
    また、コマンドラインから using Module しても補完できました。
    どうも前回確認した時にはモジュールが既に読み込まれていたようです。

    確かにモジュール内で定義されたクラスは読み込まなければ使えないので当然と言えば当然なのですが、補完するためには using Module を実行する必要があるようです。

    キャンセル

  • 2019/03/07 11:17

    ありがとうございました。
    上手くいきそうです
    クラスをISEで読み取れる形で定義しようと躍起になっていて
    ISE側を変更して現在使っているクラスを見つけられるようにする 発想は思い浮かびませんでした。

    $lastWord を使っていない理由が分からなかったのですが

    Zuishin 様 が過去にしていた回答からTabExpansion2の存在を知って
    TabExpansion2 のコードを見て
    こっちだとカーソル位置が取得できるのか...
    TabExpansionでもほしいなぁ... っと思いつつ

    このコメントで$lastWordを使ってない理由を聴こうとしたところで 気づきました。

    今回も本当に助かりました。
    ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.58%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る