简体   繁体   English

PowerShell Cmdlet的设计模式(在C#中)

[英]Design Patterns for PowerShell Cmdlets (in C#)

Working on a PowerShell Module in C# and something occurred to that seems logical but I can't find any other info to confirm or correct my assumptions. 在C#中使用PowerShell模块并且发生了一些似乎合乎逻辑但我无法找到任何其他信息来确认或更正我的假设。

Say I have one Cmdlet looks something like this 假设我有一个Cmdlet看起来像这样

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

Then say I have another Cmdlet which accepts a SomeCustomObject as a Parameter. 然后说我有另一个接受SomeCustomObject作为参数的Cmdlet。

[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);
    }
}

So I would assume that a PowerShell Cmdlet that takes in a particular parameter should usually not modify the parameter and instead create a copy if changes need to be made to that parameter. 因此,我假设接受特定参数的PowerShell Cmdlet通常不应修改参数,而是在需要对该参数进行更改时创建副本。 However I can't find any definitive opinions on this. 但是我找不到任何明确的意见。

In other words if I change my ProcessRecord above to 换句话说,如果我将上面的ProcessRecord更改为

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

If I have some PowerShell calls: 如果我有一些PowerShell调用:

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

$NewSCO AND $SCO BOTH would have been modified to the same state which seems bad, ie we'd always want the items upstream in the pipeline to retain their state so to speak so if later on we want to go back it always retains its original values when it was previously down stream of the Cmdlet that created it. $ NewSCO和$ SCO BOTH将被修改为相同的状态似乎很糟糕,即我们总是希望管道上游的项目保持其状态,所以如果以后我们想要返回它始终保留其状态原始值,它是以前创建它的Cmdlet的下游。

So instead (Assuming SomeCustomClass has a Copy Constructor) this should be the better design pattern. 所以相反(假设SomeCustomClass有一个复制构造函数),这应该是更好的设计模式。

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

Am I completely off here? 我完全离开了吗?

Would there ever be a GOOD reason ESPECIALLY in the pipeline to modify a parameter object upstream of the current Cmdlet? 管道中是否有一个很好的理由来修改当前Cmdlet上游的参数对象?

TofuBug, I completely agree with your conclusion for the case you have indicated. TofuBug,我完全赞同你所指出的案例的结论。 That is, one should consider cmdlet parameters as inviolate for the very reason you outlined, that any reasonable person reading the code would never suspect that your $SCO was modified after being passed along in the pipeline! 也就是说,出于你概述的原因,人们应该将cmdlet参数视为inviolate,任何阅读代码的合理人员都不会怀疑你的$SCO在管道传递之后被修改了!

But let's consider this a bit further. 但让我们进一步考虑这一点。 I would posit that the reason you have found no supporting discussion on this is that the vast majority of users are already treating parameters as read-only inputs without giving it any second thought, because it is the path of least resistance in PowerShell. 我认为你没有找到任何支持讨论的原因是绝大多数用户已经将参数视为只读输入而没有给予任何反思,因为它是PowerShell中阻力最小的路径。 That is, most parameters tend to be simple objects (strings, numbers, and Booleans)--even when you pass in a composite object. 也就是说,大多数参数往往是简单的对象(字符串,数字和布尔值) - 即使传入复合对象也是如此。

Let's get a bit more concrete with your example to see what I mean. 让我们通过你的例子更具体一点,看看我的意思。 Say SomeCustomClass has two properties: Say SomeCustomClass有两个属性:

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

Then your original classes might be: 那么你原来的课程可能是:

[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);
    }
}

But if instead of using the entire object as a parameter, grab the specific properties of interest. 但是,如果不使用整个对象作为参数,请抓取感兴趣的特定属性。 In the revised cmdlet below, there are now two parameters matched to the properties of the original input object with ValueFromPipelineByPropertyName . 在下面修订的cmdlet中,现在有两个参数与ValueFromPipelineByPropertyName的原始输入对象的属性匹配。 Because there is no original SomeCustomClass object to modify, you are forced to create a new one ( outputVar ). 因为没有原始的SomeCustomClass对象可以修改,所以你不得不创建一个新对象( outputVar )。 Note that in ProcessRecord , I am modifying an input parameter ( Name ) just like you did with SomeCustomObject , but here it is safe to do because it is not a "pointer" to a pipelined input, just a copy of one the input's properties. 请注意,在ProcessRecord ,我正在修改输入参数( Name ),就像使用SomeCustomObject ,但是这样做是安全的,因为它不是流水线输入的“指针”,只是输入属性的一个副本。

[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);
    }
}

If you run your example with the new Import-SomethingElse it will show what we want: the original object has not been modified. 如果您使用新的Import-SomethingElse运行示例,它将显示我们想要的内容:原始对象尚未被修改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM