是否可以在PowerShell中扩展索引器?

时间:2010-05-18 19:02:31

标签: powershell extensibility

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

2 个答案:

答案 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之前的版本,则必须更改脚本块,如图所示。