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

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

新規登録して質問してみよう
ただいま回答率
85.50%
PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

Q&A

解決済

1回答

3380閲覧

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

kamikazelight

総合スコア305

PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

0グッド

1クリップ

投稿2019/03/02 02:08

編集2019/03/08 01:45

ベストアンサー後

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

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

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

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

powershell

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

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

途中経過 2019.03.06時点

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

powershell

1Using Module .\class.psm1 2[System.Reflection.AssemblyName] $AssemblyName = "assemblyBuilder" 3 4$appDomain = [AppDomain]::CurrentDomain 5$assemblyBuilder = $appDomain.DefineDynamicAssembly( 6 $AssemblyName, 7 [System.Reflection.Emit.AssemblyBuilderAccess]::Run 8 ) 9$moduleBuilder = $assemblyBuilder.DefineDynamicModule($AssemblyName.Name) 10 11$TypeBuilder = $moduleBuilder.DefineType("kmkz.Form", [System.Reflection.TypeAttributes]::Public, [System.Windows.Forms.Form]) 12 13$TypeBuilder.CreateType() > $null 14 15$TypeBuilder.Assembly.GetTypes().NameSpace | sort -Unique | foreach { Write-Host "ネームスペース : $_" } 16Write-Host "フルネーム : $($Form.GetType().FullName)" 17 18$Form = [kmkz.Form]::new() 19$Form.ShowDialog() > $null 20

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

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

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

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

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

前提・実現したいこと

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

叶えたいこと主には2つです。
0. クラス利用時 インテリセンスの利用が可能

  1. クラスのグループ分け(NameSpace?)

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

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

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

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

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

powershell

1<# 2 ファイル名 : Class.psm1 3 の内容 4#> 5Class te2st 6{ 7 PutText() 8 { 9 Write-host "methodが実行されました。" 10 } 11}

powershell

1<# 2 ファイル名 : Class.ps1 3 の内容 4#> 5Using Module .\Class.psm1 6# Import-Module -Name .\Class.psm1 <Using Module だけで動作したため不要> 7 8$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でしか利用できない方法は使えません。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

wwbQzhMkhhgEmhU

2019/03/02 06:43

率直な感想ですが、PowerShellに拘りたい積極的な理由がないのであれば、C#で書いたら?って思いました。 私はChocolatey以外でまともにPowerShellを使い込んでいるものを見たことがありません。運用系のスクリプトをPowerShellで書くのは賛成ですが、そもそもアプリのようなものはC#で書いた方がいいかなぁ、と思うので。 私が運用系以外でPowerShellをちゃんと書いたことがあるのは、.NETの開発環境はおろか、他の言語も、インターネットすら使わせてもらえなかったときだけでした。そのときはAdd-TypeにC#のコードを書いてコンパイルさせてましたが(遅かったので)、確かにインテリセンスは欲しかったです。でもVisual Studioとかある環境でわざわざ必要かと言われたらそうは思いませんし、運用系のスクリプトがクラス云々とか出てくるしっかりしたものになっちゃったら結構おおごとなので、必要なケースが想像つかない感じです。 趣味としてやりたいのであれば、まずは他人に聞かずに進めて、ある程度他の人にも利便性が見えてきた段階から、聞いてみるとかの方がいいかもしれません。色々言いましたが、個人的には、やってることは高尚でいいことだと思いますし、応援もしてます。頑張ってください。
kamikazelight

2019/03/02 06:51 編集

そもそも論ですね ありがとうございます。 ですが 話を掘り下げたくても修正依頼に書かれてしまうと ためらってしまいます。 この内容のコピペでいいので回答欄にお願い致します。
wwbQzhMkhhgEmhU

2019/03/02 06:58

ん?いや回答になってないものを書くのは他の人に悪いので。。。 きっと回答欄に書くような内容のことをどなたか書いてくれるのではないかと期待しています。
kamikazelight

2019/03/02 07:12

そうですか、私にとってはとても興味深い話だったのですが 質問への追記・修正の依頼 で話を進めるのは気が進まないので 諦めます。
Zuishin

2019/03/08 04:18

TabExpansion2 は ISE のエディタ内では使われてないんじゃないでしょうか。 私も最初 TabExpansion2 でやろうとしてダメだった記憶があります。
kamikazelight

2019/03/08 07:54 編集

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

2019/03/08 08:00

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

2019/03/08 08:06

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

回答1

0

ベストアンサー

名前空間の件ですが、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 09:27

編集2019/03/06 10:51
Zuishin

総合スコア28656

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2019/03/06 09:39

なお、class 構文で作られたクラスは次のように取得できることがわかりました。 [AppDomain]::CurrentDomain.GetAssemblies()|?{$_.GetName().Name -eq 'PowerShell Class Assembly'}|%{$_.GetTypes()} これなら補完のたびに読み込んでもパフォーマンスは許容範囲に収まるかもしれません。
kamikazelight

2019/03/06 12:20

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

2019/03/06 15:43

再度確認してみたところ、うまく補完できませんでした。 そこで Import-Module でモジュールを読み込んだところ、補完できました。 また、コマンドラインから using Module しても補完できました。 どうも前回確認した時にはモジュールが既に読み込まれていたようです。 確かにモジュール内で定義されたクラスは読み込まなければ使えないので当然と言えば当然なのですが、補完するためには using Module を実行する必要があるようです。
kamikazelight

2019/03/07 02:17

ありがとうございました。 上手くいきそうです クラスをISEで読み取れる形で定義しようと躍起になっていて ISE側を変更して現在使っているクラスを見つけられるようにする 発想は思い浮かびませんでした。 $lastWord を使っていない理由が分からなかったのですが Zuishin 様 が過去にしていた回答からTabExpansion2の存在を知って TabExpansion2 のコードを見て こっちだとカーソル位置が取得できるのか... TabExpansionでもほしいなぁ... っと思いつつ このコメントで$lastWordを使ってない理由を聴こうとしたところで 気づきました。 今回も本当に助かりました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問