更改列表中的值

Change value in the list

为什么我无法更改列表中的对象值?

$pt = [Drawing.Point]::Empty
$pt.X = 777
$pt.X # Output: 777

$list = [Collections.Generic.List[Drawing.Point]]::new()
$list.Add([Drawing.Point]::Empty)
$list[0].X = 777
$list[0].X # Output: 0 (Expected 777)

$aList = [Collections.ArrayList]::new()
$aList.Add([Drawing.Point]::Empty)|Out-Null
$aList[0].X = 777
$aList[0].X # Output: 777

如何更改通用列表中的值?

更新:

我有这个代码,returns 我有一个“项目”列表。我应该如何编辑此代码以按应有的方式使用 Offset() 方法?

Add-Type -Ref '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()
$items[0].Location.Offset(5, 55)
$items[0].Location

或者,如果我对 mklement0 的理解正确,则无法使用 Point 对象及其方法,如果它存储在 collection/array?


在2022年,仅仅将一个对象存储在一个集合中就有这样的困难...

您发现 PowerShell 中的限制

  • In-place 更新 .NET 值类型 访问 (a) 作为类型的 属性 or field or (b) by index of a strongly typed collection 不支持.

    • [Drawing.Point].IsValueType表示手头的类型是值类型。
  • 不幸的是,non-support 沉默临时副本 146=]值或集合元素被修改,原始未修改,这就是你看到的。

一个缓解因素是 mutable .NET 值类型是 rare 并且,在事实上,甚至 officially recommended against,PowerShell 默认构造 [object[]] 个数组,其中 value-type 个实例是 boxed,即包装在本身是 .NET 引用类型 的助手 [object] 实例中,因此避免了这个问题。

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

  • 使用由 ,

    构造的 PowerShell 数组
  • 使用 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 的评论所暗示的那样,解决方法是 获取元素或 属性 / 字段的 copy值,修改它,然后用修改后的副本替换原来的元素或属性/字段值;以强类型集合为例:

# 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

同样适用于self-mutating 方法 例如.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

您更新的示例需要 两个 解决方法,因为将 value-type 集合元素(在强类型 [Item] 列表中)与值类型组合在一起将不同的值类型([System.Drawing.Point] 实例)公开为 field:

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