30

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

セットアップ:

  • ~\documents\windows\powershell\modules\holidays\ 内の holiday.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
    }
}
4

9 に答える 9

24

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


using落とし穴になりやすい

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

  • ステートメントでモジュールのフル パスを指定しない限り、このusingステートメントは、含まれていないモジュールに対しては機能しません。モジュールはステートメントを介して使用できますが、モジュールのロード方法によっては機能しない可能性があるため、これはかなり驚くべきことです。PSModulePathusingGet-Moduleusing
  • この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()一方、パイプラインに書き込まれたすべてのオブジェクトを収集し、スクリプト ブロック全体の実行が完了すると生成されます。

于 2016-11-05T18:21:46.247 に答える
15

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

using module holidays
于 2016-08-01T14:32:25.763 に答える
3

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 ドメインでアセンブリをロードするのと同じように) 初めてロードされた後、クラス定義を再ロードするために新しいランタイムを作成する必要があることです。

于 2017-01-05T16:21:21.697 に答える
1

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

于 2016-04-16T08:02:48.810 に答える