PowerShell的类型扩展工具很整洁,但我还没有找到方法 - 如果存在 - 扩展索引器。我试图为索引器属性添加ScriptProperty(在System.String的情况下为Chars),为getter(get_Chars)添加ScriptMethod,但这两种方法都没有用。这是可能的,还是我在浪费时间? :)
[编辑]显然正确的成员类型是ParameterizedProperty,但当我尝试时,我得到:
Add-Member : Cannot add a member with type "ParameterizedProperty". Specify a different
type for the MemberTypes parameter.
At line:1 char:11
+ Add-Member <<<< -MemberType ParameterizedProperty -Name Item -InputObject $string { "x" }
+ CategoryInfo : InvalidOperation: (:) [Add-Member], InvalidOperationException
+ FullyQualifiedErrorId : CannotAddMemberType,Microsoft.PowerShell.Commands.AddMemberCommand
答案 0 :(得分:0)
我将得出结论,我得到的错误信息是关于此事的最后一句话。此外,在进一步的反思中,显而易见的是,无论如何,这种机制并不支持我所希望的那种延伸。 : - )
答案 1 :(得分:0)
您无法直接在Powershell中创建ParameterizedProperty属性,但您可以通过允许Powershell将PSObject包装在具有访问者属性的对象周围来间接创建它们。然后,在要添加属性的对象上将此PSObject设置为NoteProperty。
在C#中,我们讨论的是this[]
访问者。我编写了一个Powershell脚本,它创建了一个最小的.NET对象,其中包含this[]
个访问者。为了使它尽可能通用,我试图复制ScriptProperty成员所做的事情,并且我添加了两个ScriptBlock
类型的属性 - 一个用于Get
块,另一个用于{ {1}}阻止。基本上,当用户设置Set
访问者时,它会调用this[]
块,当用户从Set
访问者检索时,它会调用this[]
块。< / p>
以下模块,我调用了 PSObjectWrappers.psm1 :
Get
请注意,我在C#代码中进行了这样的条件编译,以使代码在Powershell 4及更高版本中的行为类似于正确的ScriptBlock,因此会自动提供<#
.SUMMARY
Creates a new ParameterizedPropertyAccessor object.
.DESCRIPTION
Instantiates and returns an object compiled on the fly which provides some plumbing which allows a user to call a new Parameterized
Property, which looks as if it is created on the parent object. In fact, a NoteProperty is created on the parent object which retrieves
an instance of ParameterizedPropertyAccessor, which has a this[] accessor which Powershell wraps in a ParameterizedProperty object.
When the this[] accessor is retrieved, it tries to retrieve a value via a Get script block. When the this[] accessor is updated, this
triggers a Set script block.
.NOTES
No actual variable value state is stored by this object.
The C# code is conditionally compiled to take advantage of new functionality in Powershell 4. Before this version, the first parameter
in the Set and Get script blocks must be "[PSObject] $this". From this version, the $this parameter is automatically created for the user.
#>
Function New-ParameterizedPropertyAccessor
{
Param(
# Contains the object on which the "ParameterizedProperty" will be added.
[Parameter(Mandatory = $true, Position = 0)]
[PSObject] $Parent,
# The name of the parameterized property.
[Parameter(Mandatory = $true, Position = 1)]
[string] $Name,
# Script block which will be called when the property is retrieved.
# First parameter must be $this. Second parameter must be $key.
[Parameter(Mandatory = $true, Position = 2)]
[scriptblock] $Get,
# Script block which will be called when the property is set.
# First parameter must be $this. Second parameter must be $key. Third parameter must be $value.
[Parameter(Mandatory = $true, Position = 3)]
[scriptblock] $Set
);
# Note. You *MUST* ensure the next line starts at position 1 on the line. Likewise, the last line of the code *MUST*
# start at position 1 on the line.
$csharpCode = @'
using System;
using System.Collections.Generic;
using System.Management.Automation;
public class ParameterizedPropertyAccessor
{
private PSObject _parentPsObject;
private ScriptBlock _getBlock;
private ScriptBlock _setBlock;
public ParameterizedPropertyAccessor(PSObject parentPsObject, string propertyName, ScriptBlock getBlock, ScriptBlock setBlock)
{
_parentPsObject = parentPsObject;
PSVariable psVariable = new PSVariable(propertyName, this, ScopedItemOptions.ReadOnly);
PSVariableProperty psVariableProperty = new PSVariableProperty(psVariable);
_parentPsObject.Properties.Add(psVariableProperty);
_getBlock = getBlock;
_setBlock = setBlock;
}
public object this[object key]
{
get
{
#if WITH_CONTEXT
return _getBlock.InvokeWithContext(null, new List<PSVariable> { new PSVariable("this", _parentPsObject) }, new object[] { key });
#else
return _getBlock.Invoke(new object[] { _parentPsObject, key });
#endif
}
set
{
#if WITH_CONTEXT
_setBlock.InvokeWithContext(null, new List<PSVariable> { new PSVariable("this", _parentPsObject) }, new object[] { key, value });
#else
_setBlock.Invoke(new object[] { _parentPsObject, key, value });
#endif
}
}
}
'@;
<#
The version of the ScriptBlock object in Powershell 4 and above allows us to create automatically declared
context variables. In this case, we are providing a $this object, like you would get if we were using a
ScriptMethod or ScriptProperty member script. If we are using this version, then set the WITH_CONTEXT symbol
to conditionally compile a version of the C# code above which takes advantage of this.
#>
If ($PSVersionTable.PSVersion.Major -ge 4)
{
$compilerParameters = New-Object System.CodeDom.Compiler.CompilerParameters;
$compilerParameters.CompilerOptions = "/define:WITH_CONTEXT";
$compilerParameters.ReferencedAssemblies.Add( "System.dll" );
$compilerParameters.ReferencedAssemblies.Add( "System.Core.dll" );
$compilerParameters.ReferencedAssemblies.Add( ([PSObject].Assembly.Location) );
}
# Compiles the C# code in-memory and allows us to instantiate it.
Add-Type -TypeDefinition $csharpCode -CompilerParameters $compilerParameters;
# Instantiates the object.
New-Object ParameterizedPropertyAccessor -ArgumentList $Parent,$Name,$Get,$Set;
}
变量。否则,您必须确保每个脚本块中的第一个参数都被称为$this
。
以下是我的测试脚本 Test-PPA.ps1 :
$this
请注意,如果您使用的是Powershell 4之前的版本,则必须更改脚本块,如图所示。