当属性名称、类型和值可变时,我正在尝试创建一个自动方法来设置对象的属性。
示例:
我有一个 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/