gpt4 book ai didi

c# - 从 2D 矩阵中切片 Span 行 - 不确定为什么会这样

转载 作者:太空狗 更新时间:2023-10-29 22:25:21 25 4
gpt4 key购买 nike

我一直在寻找一种方法来从 2D 矩阵中提取切片,而无需实际重新分配复制内容,并且

public static Span<float> Slice([NotNull] this float[,] m, int row)
{
if (row < 0 || row > m.GetLength(0) - 1) throw new ArgumentOutOfRangeException(nameof(row), "The row index isn't valid");
return Span<float>.DangerousCreate(m, ref m[row, 0], m.GetLength(1));
}

我已经用这个简单的单元测试检查了这个方法,显然它有效:

[TestMethod]
public void Foo()
{
float[,] m =
{
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 9.5f, 10, 11 },
{ 12, 13, 14.3f, 15 }
};
Span<float> s = m.Slice(2);
var copy = s.ToArray();
var check = new[] { 9, 9.5f, 10, 11 };
Assert.IsTrue(copy.Select((n, i) => Math.Abs(n - check[i]) < 1e-6f).All(b => b));
}

虽然这对我来说似乎不对。我的意思是,我想了解幕后到底发生了什么,因为 ref m[x, y] 部分并不能说服我。

运行时如何获取对矩阵内该位置值的实际引用,因为二维数组中的 this[int x, int y] 方法只是返回一个值而不是引用资料?

ref 修饰符不应该只获取对返回给方法的 float 值的本地副本 的引用,而不是引用存储在矩阵中的实际值?我的意思是,否则使用 ref 返回的方法/参数将毫无意义,事实并非如此。

我查看了测试方法的 IL 并注意到了这一点:

enter image description here

现在,我不是 100% 确定,因为我不太擅长阅读 IL,但是 ref m[x, y] 调用是否被转换为对那个的调用其他 Address 方法,我想它只是自己返回一个 ref 值?

If that's the case, is there a way to directly use that method from C# code?

And is there a way to discover methods like this one, when available?

我的意思是,我只是通过查看 IL 注意到,我不知道它存在,也不知道为什么之前的代码能正常工作,此时我想知道默认库中有多少很棒的东西,却没有任何提示对于普通开发人员。

谢谢!

最佳答案

标准一维 (SZ) 阵列具有三个操作码来处理它们 - ldelemstelemldelema。它们表示可以对变量 执行的操作 - 获取其值、设置其值以及获取对它的引用。 a[i] 语法只是被翻译成任何代表你对元素所做的事情。其他变量具有相似的操作码(ldlocSTLocldlocaldfldstfld , ldflda 等)

但是,这些操作码不能用于多维数组。引用 ECMA-335:

For one-dimensional arrays that aren’t zero-based and for multidimensional arrays, the array class provides a Get method.

For one-dimensional arrays that aren’t zero-based and for multidimensional arrays, the array class provides a StoreElement [sic] method

For one-dimensional arrays that aren’t zero-based and for multidimensional arrays, the array class provides an Address method.

StoreElement 方法已重命名为 Set,但这仍然有效。访问多维数组的元素会转换为您对它们执行的任何操作。

这三个方法具有这些签名:

instance int32 int32[0...,0...]::Get(int32, int32)
instance void int32[0...,0...]::Set(int32, int32, int32)
instance int32& int32[0...,0...]::Address(int32, int32)

这些内部方法由 CLR 实现。注意最后一个方法返回的引用。虽然最近才将返回引用的功能添加到 C# 中,但 CLI 从一开始就支持它。

另请注意,在任何时候都不会涉及索引器。事实上,数组甚至没有索引器,因为那是 C# 的事情并且它不足以实现变量的所有操作,因为缺少get reference 访问器。

总而言之,数组上的 a[x] 和非数组(任何具有索引器的对象)上的 a[x] 都是 大量不同的东西。

顺便说一下,DangerousCreate 也可以工作,这要归功于这个声明(又是 ECMA-335):

Array elements shall be laid out within the array object in row-major order (i.e., the elementsassociated with the rightmost array dimension shall be laid out contiguously from lowest tohighest index). The actual storage allocated for each array element can include platform-specificpadding.

关于c# - 从 2D 矩阵中切片 Span<T> 行 - 不确定为什么会这样,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48069646/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com