2016-05-26 4 views
1

C#でPowerShellモジュールを使用していて、それが論理的に見えるものの、私の前提を確認または修正するための情報が見つかりません。 PowerShell Cmdletのデザインパターン

は、その後、私はパラメータとしてSomeCustomObjectを受け入れ、別のコマンドレットを持っていると言う、私は1コマンドレットは、この

[Cmdlet(VerbsCommon.Get, CmdletHelpers.Noun, SupportsShouldProcess = true)] 
[CmdletBinding] 
public class GetSomething : PSCmdlet 
{ 
    protected override void ProcessRecord() 
    { 
     var SomeCustomObject = new SomeCustomClass() 
     WriteObject(SomeCustomObject); 
    } 
} 

のようになります持っていると言います。

[Cmdlet(VerbsData.Import, CmdletHelpers.Noun)] 
[CmdletBinding] 
public class ImportSomething : PSCmdlet 
{ 

    [Parameter(Mandatory = true, ValueFromPipeline = true)] 
    public SomeCustomClass SomeCustomObject { get; set; } 

    protected override void ProcessRecord() 
    { 
     // Do Something with SomeCustomObject 
     // Return modified SomeCustomObject 
     WriteObject(SomeCustomObject); 
    } 
} 

だから私は、特定のパラメータにとりPowerShellのコマンドレットは通常、パラメータを変更し、代わりに変更はそのパラメータになされる必要がある場合はコピーを作成するべきではないことを前提としています。しかし、私はこれについての決定的な意見を見つけることができません。言い換えれば

私はいくつかのPowerShellを呼び出す持っている場合、私は

protected override void ProcessRecord() 
{ 
    SomeCustomObject.ChangeSomething(); 
    WriteObject(SomeCustomObject); 
} 

に上記の私のProcessRecordを変更した場合:

$SCO = Get-Something 
$NewSCO = $SCO | Import-Something 

$ NewSCOと$ SCOの両方がその同じ状態に変更されていたであろうつまり、パイプラインの上流にある項目の状態を保持して、後で元に戻したい場合は、元の値を保持していました(以前はCmdletを作成したCmdletのダウンストリームだった場合)。

代わりに(SomeCustomClassにはコピーコンストラクタがあると仮定します)、これはより良いデザインパターンになるはずです。

protected override void ProcessRecord() 
{ 
    var NewSomeCustomObject = new SomeCustomClass(SomeCustomObject); 
    NewSomeCustomObject.ChangeSomething(); 
    WriteObject(NewSomeCustomObject); 
} 

私は完全にオフですか?

現在のCmdletの上流のパラメータオブジェクトを変更するために、パイプラインに納得の理由があることはありますか?

答えて

0

TofuBug、私は、あなたが指示した場合の結論に完全に同意します。つまり、あなたが概略を示した理由から、コードを読み取る人は、$SCOがパイプラインで渡された後に変更されたことを決して疑うことはないと考えられるため、コマンドレットパラメータをinviolateとみなしてください。

これをもう少し考えてみましょう。私はこれについての議論が支持されていない理由は、大部分のユーザーが、PowerShellの抵抗が最も少ないパスであるため、パラメータを読み込み専用の入力として扱っているということです。つまり、ほとんどのパラメータは、コンポジットオブジェクトを渡しても、単純なオブジェクト(文字列、数値、ブール値)になる傾向があります。

あなたの例を少し具体的にして、私が何を意味するのか見てみましょう。

public class SomeCustomClass 
{ 
    public string Name { get; set; } 
    public string Description { get; set; } 
} 

次に、あなたのオリジナルのクラスがあるかもしれない:

[Cmdlet(VerbsCommon.Get, "Something")] 
public class GetSomething : PSCmdlet 
{ 
    protected override void ProcessRecord() 
    { 
     var SomeCustomObject = new SomeCustomClass 
     { Name = "some name", Description = "some description" }; 
     WriteObject(SomeCustomObject); 
    } 
} 

[Cmdlet(VerbsData.Import, "Something")] 
public class ImportSomething : PSCmdlet 
{ 
    [Parameter(Mandatory = true, ValueFromPipeline = true)] 
    public SomeCustomClass SomeCustomObject { get; set; } 

    protected override void ProcessRecord() 
    { 
     SomeCustomObject.Name = "new name"; // modify the input object--bad! 
     WriteObject(SomeCustomObject); 
    } 
} 

しかし、その代わりに、パラメータとしてオブジェクト全体を使用するので、興味のある特定の特性をつかむ場合SomeCustomClassは2つのプロパティを持っていると言います。下の改訂されたコマンドレットでは、元の入力オブジェクトのプロパティに一致する2つのパラメータがValueFromPipelineByPropertyNameになりました。元のSomeCustomClassオブジェクトは変更されないため、新しいオブジェクト(outputVar)を作成する必要があります。では、SomeCustomObjectと同じように入力パラメータ(Name)を変更していますが、パイプライン入力の「ポインタ」ではなく、入力プロパティのコピーの1つのみであるため安全です。

[Cmdlet(VerbsData.Import, "SomethingElse")] 
public class ImportSomethingElse : PSCmdlet 
{ 
    [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] 
    public string Name { get; set; } 

    [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] 
    public string Description { get; set; } 

    protected override void ProcessRecord() 
    { 
     Name = "new name"; 
     var outputVar = new SomeCustomClass { Name = Name, Description = Description} 
     WriteObject(outputVar); 
    } 
} 

新しいImport-SomethingElseであなたの例を実行した場合、それは我々が望むものを表示します:元のオブジェクトは変更されていません。

関連する問題