powershell - 将 "unknown"类型的 "unknown"属性设置为 "unknown"值的有效方法?

标签 powershell

当属性名称、类型和值可变时,我正在尝试创建一个自动方法来设置对象的属性。

示例:

我有一个 WinForms 表单 ( [System.Windows.Forms.Form] ),我想设置 Size , TopMost ,和Dock特性。我通过 JSON 获取这些值:

"properties": [
    {
        "name": "Size",
        "type": "System.Drawing.Size",
        "value": {
            "width": 500,
            "height": 500
        }
    },
    {
        "name": "TopMost",
        "type": "System.Boolean",
        "value": true
    },
    {
        "name": "Dock",
        "type": "System.Windows.Forms.DockStyle",
        "value": "Right"
    }
]

问题是,由于每个属性都是不同的类型,因此必须以不同的方式设置它们:

$form.Size = [System.Drawing.Size]::new(500, 500)
$form.TopMost = $true
$form.Dock = [System.Windows.Forms.DockStyle]::Right

现在我正在使用 switch声明:

switch -regex ($property.type){
    "^System\.Drawing\.Size$" {
        $form.$($property.name) = [system.drawing.size]::new($property.value.width, $property.value.height)
    }
    "^System\.Windows\.Forms\.DockStyle)$" {
        $type = [type]$($property.type)
        $form.$($property.name) = $type::$($property.value)
    }
    "^System\.(String|Boolean)$" {
        $form.$($property.name) = $property.value
    }
    Default {
        $type = [type]$($property.type)
        $form.$($property.name) = $type::new($($property.value))
    }
}

Default我使用的情况不适用于很多类型,因此每当我遇到想要使用的新类型时,我都必须更新 switch陈述。 System.Windows.Forms.FlowDirection例如,是一个类似 DockStyle 的枚举,所以我必须更新该案例声明:

"^System\.Windows\.Forms\.(DockStyle|FlowDirection))$" {
    $type = [type]$($property.type)
    $form.$($property.name) = $type::$($property.value)
}

是否有一个单一、统一的方法可以工作,无论类型如何?

最佳答案

根据评论,没有防弹的内置机制可以从 json 格式中进行反序列化。这是一个不完整的方法,尽管它对一些常见类型(枚举、值类型、具有满足某些约束的构造函数的类)进行了通用处理...

首先,设置一个测试上下文(请注意,我已经更改了表单大小,因此宽度和高度不同,因此我们可以证明它们设置正确):

$ErrorActionPreference = "Stop";
Set-StrictMode -Version "Latest";

$properties = @"
{
  "properties": [
    {
      "name": "Size",
      "type": "System.Drawing.Size",
      "value": {
        "width": 1024,
        "height": 768
      }
    },
    {
      "name": "TopMost",
      "type": "System.Boolean",
      "value": true
    },
    {
      "name": "Dock",
      "type": "System.Windows.Forms.DockStyle",
      "value": "Right"
    }
  ]
}
"@ | ConvertFrom-Json;

Add-Type -Assembly "System.Windows.Forms";
$form = new-object System.Windows.Forms.Form;

然后我们可以使用反射来检查 json 中每个项目的表单属性。

根据表单属性的类型,我们可以进行一些通用处理,在一些限制下 - 例如,对于对象属性,必须是一个公共(public)构造函数参数名称与 json 中的字段匹配的类型。如果给定类型不是这样,您仍然需要为该类型添加一个特殊的处理程序...

foreach( $property in $properties.properties )
{
    write-host $property.name;

    # needs error handling!
    $propertyType = $form.GetType().GetProperty($property.Name).PropertyType;

    # handle simple types
    if( $propertyType -in @( [bool], [string], [int32], [int64] ) )
    {
        write-host "    simple type";
        write-host "    $($propertyType.FullName)";
        write-host "    '$($property.value)'";
        $form.($property.Name) = $property.value;
        continue;
    }

    # is the form property an enum?
    if( $propertyType.IsEnum )
    {
        # powershell will automatically convert a string to the appropriate enum type
        write-host "    enum";
        write-host "    $($propertyType.FullName)";
        write-host "    '$($property.value)'";
        $form.($property.Name) = $property.value;
        continue;
    }

    # can we create an object using a constructor?
    if( $property.value -is [System.Management.Automation.PSCustomObject] )
    {
        write-host "    constructor";
        write-host "    $($propertyType.FullName)";
        # can we find an appropriate constructor for the type?
        $valueNames = @( $property.value.psobject.Properties.Name );
        $constructors = @(
            $propertyType.GetConstructors() `
                | where-object {
                    $params = $_.GetParameters();
                    # not the right constructor if it's got a different number of parameters
                    if( -not ($params.Length -eq $valueNames.Length ) )
                    {
                        return $false;
                    }
                    # are there any constructor parameters
                    # that don't appear in the json?
                    $missing = $params | where-object {
                        $valueNames -notcontains $_.Name
                    }
                    return ($null -eq $missing)
                }
        );
        if( $constructors.Length -ne 1 )
        {
            throw "couldn't match to exactly one constructor";
        }
        write-host "    $($constructor.ToString())";
        # note - we don't verify json value types are compatible with the constructor parameter types
        $paramValues = @(
            $constructor.GetParameters() | foreach-object {
                # use "-as" to cast the json value to the correct type for the constructor parameter
                write-host "    $($_.ParameterType.Name) $($_.Name) = '$($property.value.($_.Name))'";
                $property.value.($_.Name) -as $_.ParameterType
            };
        )
        $form.($property.Name) = $constructors[0].Invoke($paramValues);
        continue;
    }

    # don't know what to do with this type
    throw "unhandled property type '$($propertyType.FullName)'";
   
}

日志输出是:

Size
    constructor
    System.Drawing.Size
    Void .ctor(Int32, Int32)
    Int32 width = '1024'
    Int32 height = '768'
TopMost
    simple type
    System.Boolean
    'True'
Dock
    enum
    System.Windows.Forms.DockStyle
    'Right'

生成的表单如下所示:

$form | fl Size, TopMost, Dock

# Size    : {Width=1024, Height=768}
# TopMost : True
# Dock    : Right

这应该适用于大多数常见的System.Windows.Forms.Form属性,但如果您尝试将其用作一般属性,可能会很快失败 -用于其他类型的用途解串器...

关于powershell - 将 "unknown"类型的 "unknown"属性设置为 "unknown"值的有效方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76377899/

相关文章:

rest - Azure 网站 Kudu REST API - 身份验证

powershell - 使用 psexec 发送多个命令

c# - 如何从 .Net 程序运行 Import-PSSessions

PowerShell - 缩短命名空间名称,以便更轻松地访问类型

powershell - Azure 资源模板部署问题

winforms - 在 PowerShell 中为新窗体分离任务栏图标

sql-server - PowerShell:使用 Get-Credential 调用 sqlcmd 不起作用

c# - 术语 'Add-AzureAccount' 未被识别为 cmdlet、函数、脚本文件的名称

powershell - 为什么使用 Get-Variable 访问参数变量的属性只能在 ISE 中第一次工作?

powershell - 测试 WinRM/WSMan 连接?