2

Из-за проблем, которые у меня возникают с классами PowerShell (не имеющих аксессоров и мутаторов, таких как getter и setters), я использую пользовательские объекты, созданные с помощью add-member. У меня возникла проблема с поиском правильного способа ссылки на значения массива в SecondValue. ScriptBlock.

Для простых предметов следующий код работает нормально

$Object = New-Object PSObject
Add-Member -InputObject $Object -MemberType NoteProperty -Name "Array" -Value @() -Force
$Object.Array += 1
$Object.Array
$Object.Array[0] = 2

Но так как я хочу, чтобы параметры добавления (проверка параметров, настройки дополнительных связанных свойств) при установке значения я использовал следующий формат

$Object2 = New-Object PSObject
Add-Member -InputObject $Object2 -MemberType ScriptProperty -Name "Array" -Value {@($this.ArrayData)} -SecondValue{
    param($NewValue)
    $this.ArrayData = $NewValue}
Add-Member -InputObject $Object2 -MemberType NoteProperty -Name "ArrayData" -Value @() -Force

Это не работает, как ожидалось, потому что я не знаю, как включить индекс в объявление SecondValue. У кого-нибудь есть какие-либо идеи?

1 ответ1

0

К сожалению, нет возможности параметризовать свойства скрипта. Мы можем убедиться в этом, прочитав источник PowerShell. Внутренне учет свойств скрипта хранится в объектах PSScriptProperty . Когда значения таких свойств запрашиваются или изменяются, вызываются частные функции InvokeGetter или InvokeSetter , соответственно. InvokeSetter запускает блок сценария установки с новым значением в качестве единственного аргумента (как видно в последней строке этого фрагмента):

SetterScript.DoInvokeReturnAsIs(
    useLocalScope: true,
    errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToExternalErrorPipe,
    dollarUnder: AutomationNull.Value,
    input: AutomationNull.Value,
    scriptThis: scriptThis,
    args: new object[] { value });

InvokeGetter запускает блок сценария получения без аргументов вообще:

return GetterScript.DoInvokeReturnAsIs(
    useLocalScope: true,
    errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.SwallowErrors,
    dollarUnder: AutomationNull.Value,
    input: AutomationNull.Value,
    scriptThis: scriptThis,
    args: Utils.EmptyArray<object>());

Поэтому мы не можем передавать дополнительную информацию получателю или установщику. (scriptThis относится только к $this , объекту, для которого мы устанавливаем свойство.)

Существует обходной путь: командлет Add-Type с параметром -TypeDefinition . Вы можете встроить некоторый код C # (или VB.NET, если хотите), который определяет индексируемый тип:

Add-Type -TypeDefinition @"
using System;
using System.Runtime.CompilerServices;
public class SomeClass {
    private int[] myArray;
    public SomeClass(int Capacity) {
        myArray = new int[Capacity];
    }
    [IndexerName("ArrayData")] public int this[int index] {
        get {
            Console.WriteLine("Somebody asked for the element at index " + index.ToString() + "!");
            return myArray[index];
        }
        set {
            if (value < 0) throw new InvalidOperationException("Negative numbers not allowed");
            if (index == 0) throw new InvalidOperationException("The first element cannot be changed");
            myArray[index] = value;
        }
    }
}
"@

Затем вы можете сделать что-то вроде этого:

$obj = [SomeClass]::new(5)
$obj[3] = 255
Write-Host $obj[3] # Prints the "somebody accessed" notice, then 255

Или вы можете воспользоваться именем индексатора и сделать это:

$obj.ArrayData(3) = 255 # Note the parentheses, not brackets
Write-Host $obj.ArrayData(3) # Prints the notice, then 255

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .