他のいくつかのスクリプトのライブラリのようにモジュールをセットアップしました。 Import-Module
を呼び出すスクリプトスコープにクラス宣言を取得する方法がわかりません。 Export-Module
のように、-class
引数を使用して-function
を配置しようとしましたが、-class
がありません。すべてのスクリプトでクラスを宣言する必要がありますか?
セットアップ:
import-module holidays
を呼び出しますクラスは次のようになります。
Class data_block
{
$array
$rows
$cols
data_block($a,$r,$c)
{
$this.array = $a
$this.rows = $r
$this.cols = $c
}
}
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()
は、パイプラインに書き込まれたすべてのオブジェクトを収集し、スクリプトブロック全体の実行が完了した後にのみ生成されます。
「モジュールを使用する」必要なしにクラスをロードする方法を見つけました。 MyModule.psd1ファイルで次の行を使用します。
ScriptsToProcess = @('Class.ps1')
そして、クラスをClass.ps1ファイルに入れます。
class MyClass {}
更新:このメソッドで「モジュールMyModuleを使用する」を使用する必要はありませんが、次のいずれかを実行する必要があります。
これは確かに期待どおりに機能しません。
PS 5のアイデアは、.psm1拡張子を持つ別のファイルでクラスを定義できることです。
次に、コマンド(例)を使用して定義をロードできます。
using module C:\classes\whatever\path\to\file.psm1
これは、スクリプトの最初の行でなければなりません(コメントの後)。
クラスの定義がスクリプトから呼び出されても、セッション全体でモジュールがロードされるので、非常に苦痛です。次のコマンドを実行すると、これを確認できます。
get-module
ロードしたファイルの名前が表示されます。スクリプトを再度実行しても、[〜#〜] not [〜#〜]クラス定義を再ロードします! (psm1ファイルを読み取らないこともあります。)これにより、歯がぎざぎざになります。
時々-時々-スクリプトを実行する前にこのコマンドを実行できます。これにより、更新されたクラス定義でモジュールがリロードされます:
remove-module file
ここで、fileはパスまたは拡張子なしの名前です。ただし、健全性を保つために、PSセッションを再起動することをお勧めします。これは明らかに厄介です。マイクロソフトはこれをどうにかして整理する必要があります。
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ドメインにアセンブリをロードするのと同じように)が初めてロードされた後にクラス定義を再ロードするために新しいランタイムを作成する必要があることです。
あなたはほとんどできません。 about_Classes
ヘルプによると:
クラスキーワード
新しいクラスを定義します。これは、真の.NET Frameworkタイプです。クラスメンバーはパブリックですが、モジュールスコープ内でのみパブリックです。タイプ名を文字列として参照することはできません(たとえば、New-Objectは機能しません)。また、このリリースでは、スクリプト外でタイプリテラル(たとえば、[MyClass])を使用できません。クラスが定義されているモジュールファイル。
つまり、data_block
インスタンスを取得したり、それらのクラスを操作する関数を使用したりする場合は、New-DataBlock
などの関数を作成して、新しいdata_block
インスタンスを返すようにします。次に、を使用して、クラスのメソッドとプロパティ(静的なものを含む可能性が高い)を取得できます。
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;}
開発中にクラス定義を更新するには、クラスのコードを選択し、F8
を押して選択したコードを実行します。 -Force
コマンドのImport-Module
オプションほどクリーンではありません。 「Module
の使用」にはそのオプションはなく、Remove-Module
は散発的ですが、これは、ISEを閉じずにクラスを開発して結果を確認するための最良の方法です。もう一度起動します。
この問題を回避する方法は、カスタムクラス定義を同じ名前の空の.ps1ファイルに(Java/C#の場合と同様に)移動し、それをモジュール定義と依存コードの両方にロードすることです。ドットソーシング。私はこれは素晴らしいことではないことを知っていますが、私にとっては、複数のファイルにわたって同じクラスの複数の定義を維持する必要があるよりはましです...
私はこのページのすべてのアプローチを試しましたが、繰り返し機能するものを見つけるまでにまだあります。私はモジュールキャッシュがこれで最も苛立たしい側面だと思います...クラスをモジュールとしてインポートし、コードを機能させることさえできますが、Powershellは新しいクラスを断続的にしか認識しません。 。