web-dev-qa-db-ja.com

powershell v5モジュールでクラスをエクスポートする方法

他のいくつかのスクリプトのライブラリのようにモジュールをセットアップしました。 Import-Moduleを呼び出すスクリプトスコープにクラス宣言を取得する方法がわかりません。 Export-Moduleのように、-class引数を使用して-functionを配置しようとしましたが、-classがありません。すべてのスクリプトでクラスを宣言する必要がありますか?

セットアップ:

  • 〜\ documents\windows\powershell\modules\holidays \にあるholidays.psm1
  • アクティブスクリプトがimport-module holidaysを呼び出します
  • クラスオブジェクトを正しく返すHolidays.psm1に別の関数がありますが、インポート後にアクティブスクリプトからクラスの新しいメンバーを作成する方法がわかりません

クラスは次のようになります。

Class data_block
{
    $array
    $rows
    $cols
    data_block($a,$r,$c)
    {
        $this.array = $a
        $this.rows = $r
        $this.cols = $c
    }
}
25
jason

herehere によると、PowerShell 5で次のようにすることで、モジュールで定義されたクラスを使用できます。

using module holidays
14
Lars Truijens

PSA:クラスの古いコピーをメモリに保持する既知の問題があります。それについて知らなければ、クラスでの作業は本当に混乱します。あなたはそれについて読むことができます ここ


usingは落とし穴になりやすい

usingキーワードには、次のようなさまざまな落とし穴があります。

  • usingステートメントは、PSModulePathステートメントでモジュールの完全パスを指定しない限り、usingにないモジュールでは機能しません。モジュールは_Get-Module_を介して利用できますが、モジュールのロード方法によってはusingステートメントが機能しない場合があるため、これはかなり意外です。
  • usingステートメントは、「スクリプト」の最初にのみ使用できます。 [scriptblock]::Create()または_New-Module_の組み合わせでこれを克服することはできません。 _Invoke-Expression_に渡される文字列は、一種のスタンドアロンスクリプトとして機能するようです。そのような文字列ソートの最初のusingステートメント。つまり、_Invoke-Expression "using module $path"_は成功する可能性がありますが、モジュールのコンテンツを利用できるようにするスコープはかなりわかりにくいようです。たとえば、_Invoke-Expression "using module $path"_がPesterスクリプトブロック内で使用されている場合、モジュール内のクラスは同じPesterスクリプトブロックから使用できません。

上記のステートメントは この一連のテスト に基づいています。

ScriptsToProcessプライベートモジュール関数へのアクセスを防止

モジュールマニフェストのScriptsToProcessによって参照されるスクリプトでクラスを定義すると、一見するとモジュールからクラスをエクスポートするように見えます。ただし、クラスをエクスポートする代わりに "モジュールではなくグローバルSessionStateにクラスを作成するため、プライベート関数にアクセスできません" 。私の知る限り、ScriptsToProcessを使用することは、次のようにモジュールの外部でクラスを定義することに似ています。

_#  this is like defining c in class.ps1 and referring to it in ScriptsToProcess
class c {
    [string] priv () { return priv }
    [string] pub  () { return pub  }
}

# this is like defining priv and pub in module.psm1 and referring to it in RootModule
New-Module {
    function priv { 'private function' }
    function pub  { 'public function' }
    Export-ModuleMember 'pub'
} | Import-Module

[c]::new().pub()  # succeeds
[c]::new().priv() # fails
_

これを呼び出すと、

_public function
priv : The term 'priv' is not recognized ...
+         [string] priv () { return priv } ...
_

モジュールがインポートされたときに定義されたクラスからprivが呼び出されても、モジュール関数privはクラスからアクセスできません。これはあなたが望むものかもしれませんが、クラスメソッドは通常、非公開にしたいモジュール内のいくつかの関数にアクセスする必要があることがわかったので、その使用法を見つけられませんでした。

.NewBoundScriptBlock()は確実に機能しているようです

クラスを含むモジュールにバインドされたスクリプトブロックを呼び出すことは、クラスのインスタンスをエクスポートするために確実に機能するようであり、usingが行う落とし穴に悩まされることはありません。クラスを含み、インポートされたこのモジュールを考えてみましょう:

_New-Module 'ModuleName' { class c {$p = 'some value'} } |
    Import-Module
_

モジュールにバインドされたスクリプトブロック内で[c]::new()を呼び出すと、タイプ_[c]_のオブジェクトが生成されます。

_PS C:\> $c = & (Get-Module 'ModuleName').NewBoundScriptBlock({[c]::new()})
PS C:\> $c.p
some value
_

慣用的な代替手段.NewBoundScriptBlock()

.NewBoundScriptBlock()の代わりに、もっと短い慣用的な方法があるようです。次の2行はそれぞれ、_Get-Module_によるモジュール出力のセッション状態でスクリプトブロックを呼び出します。

_& (Get-Module 'ModuleName').NewBoundScriptBlock({[c]::new()})
& (Get-Module 'ModuleName') {[c]::new()}}
_

後者には、オブジェクトがパイプラインに書き込まれたときにスクリプトブロックの中間スクリプトブロックに制御のフローをもたらすという利点があります。一方、.NewBoundScriptBlock()は、パイプラインに書き込まれたすべてのオブジェクトを収集し、スクリプトブロック全体の実行が完了した後にのみ生成されます。

20
alx9r

「モジュールを使用する」必要なしにクラスをロードする方法を見つけました。 MyModule.psd1ファイルで次の行を使用します。

ScriptsToProcess = @('Class.ps1')

そして、クラスをClass.ps1ファイルに入れます。

class MyClass {}

更新:このメソッドで「モジュールMyModuleを使用する」を使用する必要はありませんが、次のいずれかを実行する必要があります。

  • 「モジュールMyModuleを使用」を実行します
  • または、「Import-Module MyModule」を実行します
  • または、モジュール内の任意の関数を呼び出します(そのため、途中でモジュールが自動インポートされます)
18
ili

これは確かに期待どおりに機能しません。
PS 5のアイデアは、.psm1拡張子を持つ別のファイルでクラスを定義できることです。
次に、コマンド(例)を使用して定義をロードできます。

   using module C:\classes\whatever\path\to\file.psm1

これは、スクリプトの最初の行でなければなりません(コメントの後)。


クラスの定義がスクリプトから呼び出されても、セッション全体でモジュールがロードされるので、非常に苦痛です。次のコマンドを実行すると、これを確認できます。

    get-module

ロードしたファイルの名前が表示されます。スクリプトを再度実行しても、[〜#〜] not [〜#〜]クラス定義を再ロードします! (psm1ファイルを読み取らないこともあります。)これにより、歯がぎざぎざになります。


時々-時々-スクリプトを実行する前にこのコマンドを実行できます。これにより、更新されたクラス定義でモジュールがリロードされます:

    remove-module  file

ここで、fileはパスまたは拡張子なしの名前です。ただし、健全性を保つために、PSセッションを再起動することをお勧めします。これは明らかに厄介です。マイクロソフトはこれをどうにかして整理する必要があります。

4
0xG

V5でもPowerShellクラスに関して複数の問題が発生しました。

これは.netおよびPowerShellと完全に互換性があるため、今のところ次の回避策を使用することにしました。

Add-Type -Language CSharp -TypeDefinition @"
namespace My.Custom.Namespace {
    public class Example
    {
        public string Name { get; set; }
        public System.Management.Automation.PSCredential Credential { get; set; }
        // ...
    }
}
"@

利点は、型定義を追加するためにカスタムアセンブリが必要ないことです。PowerShellスクリプトまたはモジュールにインラインでクラス定義を追加できます。

唯一の欠点は、(c#/。netドメインにアセンブリをロードするのと同じように)が初めてロードされた後にクラス定義を再ロードするために新しいランタイムを作成する必要があることです。

3
oɔɯǝɹ

あなたはほとんどできません。 about_Classesヘルプによると:

クラスキーワード

新しいクラスを定義します。これは、真の.NET Frameworkタイプです。クラスメンバーはパブリックですが、モジュールスコープ内でのみパブリックです。タイプ名を文字列として参照することはできません(たとえば、New-Objectは機能しません)。また、このリリースでは、スクリプト外でタイプリテラル(たとえば、[MyClass])を使用できません。クラスが定義されているモジュールファイル。

つまり、data_blockインスタンスを取得したり、それらのクラスを操作する関数を使用したりする場合は、New-DataBlockなどの関数を作成して、新しいdata_blockインスタンスを返すようにします。次に、を使用して、クラスのメソッドとプロパティ(静的なものを含む可能性が高い)を取得できます。

3
Vesper

sing ステートメントは、それが機能するかどうかを判断する方法です。それ以外の場合、これも機能するようです。

testclass.psm1

関数を使用してクラスを提供する

class abc{
    $testprop = 'It Worked!'
    [int]testMethod($num){return  $num * 5}
}

function new-abc(){
    return [abc]::new()
}

Export-ModuleMember -Function new-abc

someScript.ps1

Import-Module path\to\testclass.psm1
$testclass = new-abc
$testclass.testProp        # returns 'It Worked!'
$testclass.testMethod(500) # returns 2500


$testclass | gm

Name        MemberType Definition
----        ---------- ----------
Equals      Method     bool Equals(System.Object obj)
GetHashCode Method     int GetHashCode()
GetType     Method     type GetType()
testMethod  Method     int testMethod(System.Object num)
ToString    Method     string ToString()
testprop    Property   System.Object testprop {get;set;}
2
Jakobii

開発中にクラス定義を更新するには、クラスのコードを選択し、F8を押して選択したコードを実行します。 -ForceコマンドのImport-Moduleオプションほどクリーンではありません。 「Moduleの使用」にはそのオプションはなく、Remove-Moduleは散発的ですが、これは、ISEを閉じずにクラスを開発して結果を確認するための最良の方法です。もう一度起動します。

1
DavSum

この問題を回避する方法は、カスタムクラス定義を同じ名前の空の.ps1ファイルに(Java/C#の場合と同様に)移動し、それをモジュール定義と依存コードの両方にロードすることです。ドットソーシング。私はこれは素晴らしいことではないことを知っていますが、私にとっては、複数のファイルにわたって同じクラスの複数の定義を維持する必要があるよりはましです...

1
Steve Rathbone

私はこのページのすべてのアプローチを試しましたが、繰り返し機能するものを見つけるまでにまだあります。私はモジュールキャッシュがこれで最も苛立たしい側面だと思います...クラスをモジュールとしてインポートし、コードを機能させることさえできますが、Powershellは新しいクラスを断続的にしか認識しません。 。

0
user5855178