binary modules vs. script modules
Having built a number of PowerShell modules (both binary and script), there's still an inconsistency between the two models that I cannot quite get my head around. Perhaps one of you could shed some light on this matter.
Imagine a function that accepts a single DateTime
value. In a script function, I would define this as a [DateTime]
parameter; in the C# function the parameter would be of type DateTime
. So far, so good.
Now imagine passing a DateTime
to the function, to which an additional note property has been added using Add-Member
. Despite being defined as a [DateTime]
, the script function parameter would gladly accept this value, since it's actually a PSObject
wrapping the original DateTime
(and potentially containing additional members) which is unwrapped upon use - I hope I'm using the correct terminology here. As expected, passing something other than a (wrapped) DateTime
would fail, thus making the function more or less type-safe.
The equivalent C# function defines the parameter as a DateTime
, so AFAIK it has no way to access the additional parameter. After all, the only "interface" that the parameter provides comes from the DateTime
type.
Alternatively, I could define the C# function's parameter type as a PSObject
, but then I would have to do my own type checking for the PSObject
's BaseObject
.
Is there a flaw in my logic? Or, more importantly, is there a way around this, so that I can still leave my binary module's type checking to PowerShell?
Many thanks in advance!
You're both right and wrong - it entirely depends on whether the target parameter is of a value type ( System.DateTime
is a struct for example) - in which case everything is lost on type coercion during parameter binding.
If, however, the parameter type is of a reference type you can "resurrect" the PSObject wrapper using PSObject.AsPSObject()
.
I came up with the following example in pure(-ish) PowerShell for ease of replicability, but I believe it adequately shows my point
Paste the following into a C# source file (say, TestCmdlets.cs
):
using System;
using System.Management.Automation;
namespace TestPSObject
{
// This will be our parameter type
public class TestObject {}
// This will be our reference type test cmdlet
[Cmdlet(VerbsDiagnostic.Test, "PSObjectByRef")]
public class TestPSObjectByRefCommand : Cmdlet
{
[Parameter(Mandatory=true)]
public TestObject TestObject
{
get { return testObject; }
set { testObject = value; }
}
private TestObject testObject;
protected override void ProcessRecord()
{
// If this works, we should receive an object with
// identical psextended properties
WriteObject(PSObject.AsPSObject(this.TestObject));
}
}
// This will be our value type test cmdlet
[Cmdlet(VerbsDiagnostic.Test, "PSObjectByValue")]
public class TestPSObjectByValueCommand : Cmdlet
{
[Parameter(Mandatory=true)]
public DateTime DateTime
{
get { return dateTime; }
set { dateTime = value; }
}
private DateTime dateTime;
protected override void ProcessRecord()
{
// If this works, we should receive an object with
// identical psextended properties (hint: we won't)
WriteObject(PSObject.AsPSObject(this.DateTime));
}
}
}
Now, in your shell, compile and import our test module:
Add-Type -Path .TestCmdlets.cs -OutputAssembly TestPSObject.dll -OutputType Library
Import-Module .TestPSObject.dll
Next up we create our test subjects and add a note property to them:
$TestObject = New-Object TestPSObject.TestObject
$TestObject |Add-Member -MemberType NoteProperty -Name TestProperty -Value "Hi there!"
$DateTime = Get-Date
$DateTime |Add-Member -MemberType NoteProperty -Name TestProperty -Value "Hi there!"
They now both return the string value Hi there!
when you dereference the TestProperty
member.
Now for the actual test:
$TestObjectAfter = Test-PSObjectByRef -TestObject $TestObject
$DateTimeAfter = Test-PSObjectByValue -DateTime $DateTime
This will still return Hi there!
:
$TestObjectAfter.TestProperty
But this will not:
$DateTimeAfter.TestProperty
链接地址: http://www.djcxy.com/p/41218.html
上一篇: 在日志文件中搜索
下一篇: 二进制模块与脚本模块