mkl*_*nt0 6

在 PowerShell 中看到了一个限制

不支持对 (a) 作为类型的属性或字段或 (b) 通过强类型集合的索引访问的.NET值类型进行就地更新

[Drawing.Point].IsValueType表示手头的类型是值类型。

不幸的是,不支持是沉默的:属性/字段值或集合元素的临时、临时副本被修改,而原始文件未修改,这就是您所看到的。

虽然注意到此限制本身不是文档的一部分,但在此 Wiki 条目中间接提到了它;更直接的讨论在GitHub 文档问题 #5833中。

至于此限制的技术原因,请参阅GitHub 问题 #12411 中的此评论;简而言之:在 PowerShell 中访问值类型实例总是意味着对其副本进行操作。

一个缓解因素是可变的 .NET 值类型很少见,事实上,甚至官方也不建议使用,并且 PowerShell[object[]]默认构造数组,其中值类型实例被装箱,即包装在一个[object]本身是 . NET引用类型,因此避免了该问题。

因此,集合的解决方法:使用[object]-typed/-storing 集合而不是强类型集合:

使用构造的 PowerShell 数组,

Use System.Collections.ArrayList,它将其元素存储为[object]实例(如您的代码所示,这也避免了该问题。

正如Theo所说,[object]键入通用列表 ( [System.Collections.Generic.List[object]]::new()) 也是一种有效的解决方法。

以下示例代码显示了哪些功能有效,哪些无效:

# These do NOT work as expected, due to being strongly typed, and the
# type being a mutable *value type*.
[Drawing.Point[]] @([Drawing.Point]::Empty),
[Collections.Generic.List[Drawing.Point]] @([Drawing.Point]::Empty),
# These DO work as expected, thanks to boxing.
[object[]] @([Drawing.Point]::Empty),  # Note: @(...) implicitly creates [object[]]
[Collections.ArrayList] @([Drawing.Point]::Empty),
[Collections.Generic.List[object]] @([Drawing.Point]::Empty) |
  ForEach-Object {
    $_[0].X = 42  # Try to update the 1st element in place.
    $_[0].X       # Output the potentially updated value.
  }

输出:

0     # !! Assignment was in effect ignored.
0     # !! "
42
42
42

将可变值类型公开为属性或字段的类型以及无法避免使用强类型集合的解决方法:

正如zett42的评论所暗示的,解决方法是获取元素或属性/字段值的副本,对其进行修改,然后将原始元素或属性/字段值替换为修改后的副本;以强类型集合为例:

# Create a strongly typed collection with a mutable value type.
$arr = [Drawing.Point[]] @([Drawing.Point]::Empty)

# Get a copy of the element, modify it, replace the original
# element with the copy.
$element0Copy = $arr[0]; $element0Copy.X = 42; $arr[0] = $element0Copy

$arr[0].X  # -> 42

自变异方法也是如此,例如.Offset()

$arr = [Drawing.Point[]] @([Drawing.Point]::Empty)

# Get a copy of the element, modify it, replace the original
# element with the copy.
$element0Copy = $arr[0]; $element0Copy.Offset(42, 43); $arr[0] = $element0Copy

$arr[0]  # -> X = 42, Y = 43

由于将值类型集合元素(在强类型列表中)与将不同值类型(实例)公开为字段的值类型相结合,您更新的示例需要两种解决方法:[Item][System.Drawing.Point]

Add-Type -ReferencedAssemblies System.Drawing '
using System;
using System.Drawing;
using System.Collections.Generic;

public class MyClass {
    public struct Item {
        public string Name;
        public Point Location;
    }

    public static List<Item> result = new List<Item> { };

    public static List<Item> foo() {
        result.Add(new Item
        {
            Name = "One",
            Location = new Point(12, 15),
        });
        result.Add(new Item
        {
            Name = "Two",
            Location = new Point(17, 22),
        });

        if(result[0].Location.X == 12){Console.WriteLine("ok");}

        return result;
    }
}'

$items = [MyClass]::foo()

# Get a copy of the [Item] element at index 0
$itemCopy = $items[0]

# Get a copy of the Location field value (of type [Point])...
$locationCopy = $itemCopy.Location
# ... and update it in place.
$locationCopy.Offset(5 ,55)

# Assign the modified [Point] back to the Location field.
$itemCopy.Location = $locationCopy

# Replace the first element with the modified [Item] copy.
$items[0] = $itemCopy

$items[0].Location # -> X = 17, Y = 70

更多推荐

列表中