更改列表中的值
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=]值或集合元素被修改,原始未修改,这就是你看到的。
虽然注意到此限制本身并不是文档的一部分,但在 this Wiki entry; a more direct discussion is in GitHub docs issue #5833.
中间接 提到了它
关于此限制的技术原因,参见this comment in GitHub issue #12411;简而言之:在 PowerShell 中访问 value-type 实例总是意味着对它的 copy 进行操作。
一个缓解因素是 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
为什么我无法更改列表中的对象值?
$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=]值或集合元素被修改,原始未修改,这就是你看到的。
虽然注意到此限制本身并不是文档的一部分,但在 this Wiki entry; a more direct discussion is in GitHub docs issue #5833.
中间接 提到了它关于此限制的技术原因,参见this comment in GitHub issue #12411;简而言之:在 PowerShell 中访问 value-type 实例总是意味着对它的 copy 进行操作。
一个缓解因素是 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