与显式类型参数相比,使用 `object` 参数时 C# NumSharp 未定义的行为

C# NumSharp undefined behavior when using `object` parameter compared to explicit type parameter

我正在使用 NumSharp 并且对它有点陌生。我想设置多个数据值,主要关注两个函数:

NDArray.SetValue(object value, param int[] indices)
NDArray.SetData(object value, param int[] indices)

第一个 SetValue 需要单值元素的完整索引,例如,如果你有 np.zeros(3,3) 它需要完整索引,如果缺少索引它将使用 0 取而代之。

例如:

var arr = np.zeros(3,3);
arr.SetValue(10.0, 0); 
arr.SetValue(10.0, 0, 0); 

两行是等价的。

另一方面,SetData会根据索引修改整个子数组,如果你给出完整的索引,它的工作方式类似于SetValue。例如:

arr.SetData(10.0, 0); // sets all arr[0] values to 10
arr.SetData(10.0, 0, 1); // sets arr[0][1] to 10

现在,对于我的用例,我想重新定义一个接受多个值并将每个值视为子数组索引的函数。

arr.SetMultiData(10.0, 0, 1); /// sets arr[0] and arr[10] values to 10

当然,我选择了扩展方法:

public static class Extensions
{
    public static void SetMultiData(this NDArray arr, object value, int[] indices)
    {
        foreach (var i in indices)
            arr.SetData(value, i);
    }
}

但这就是让我完全迷失的地方。此代码不使用扩展方法,有效:

    static void Main(string[] args)
    {
        NDArray arr = np.zeros(3, 3);
        int[] indices = new int[] { 0, 1 };
        double value = 10;

        foreach (var i in indices)
            arr.SetData(value, i); 

        Console.WriteLine(arr.ToString());
    }

输出:

[[10, 10, 10],[10, 10, 10],[0, 0, 0]]

此代码不会:

    static void Main(string[] args)
    {
        NDArray arr = np.zeros(3, 3);
        int[] indices = new int[] { 0, 1 };
        double value = 10;

        arr.SetMultiData(value, indices);

        Console.WriteLine(arr.ToString());
    }

输出:

[[10, 0, 0],[0, 0, 0],[0, 0, 0]]

这并不总是会打印,在大多数情况下它只是无法打印,我相信底层 arr 结构已相应损坏,这会导致当我 运行 它时出现未定义的行为多次。

现在,最后一点。当我将 Extension 方法更改为接受 double 而不是 object 时,它工作得很好。但是我需要扩展方法来处理不止一种类型,因为它只是 SetData 的包装器。鉴于此,我也尝试将方法更改为通用的:

public static class Extensions
{
    public static void SetMultiData<T>(this NDArray arr, object value, int[] indices)
    {
        T val = (T)value;
        foreach (var i in indices)
            arr.SetData(val, i);
    }
}

当这不起作用并产生相同的输出时,我感到很震惊。基本上,我进行了从对象到类型的显式转换。我这样调用函数:arr.SetMultiData<double>(value, indicies); 我使用了调试器并比较了使用它的代码和类型与传递给 arr.SetValue 给定 i = intval = double,但一个有效,一个无效。

是什么导致了这种行为?为什么当我明确地将它设置为 double 时,当它使用通用/object 类型都失败时,为什么它可以工作?

供参考,这里是开源的 SetData implementation.

原因是 double 可以隐式转换为 NDArray(通过 NDArray 中定义的隐式运算符)。所以当你这样做时:

arr.SetData(value, i);

value 是类型 double - 被调用的重载是 SetData(NDArray, int[),因为它是最合适的,因为 double 可以转换为 NDArray.

然而,当 value 是类型 object 甚至是未解析的泛型类型 (T) - 然后被调用的重载是 SetData(object, int[]),所以事情变得疯狂 (因为如果您作为对象传递的值不是 NDArray,那么这些重载会做完全不同的事情,而且它不是 - 它是双倍的)。重载是在编译时选择的,即使在泛型情况下,编译器也无法选择 NDArray 重载(因为选择的重载应该适合您可以传递的任何可能的泛型类型)。

在您的情况下,您可以通过在扩展方法中使用 NDArray value 而不是对象或泛型来“解决”此问题:

public static class Extensions
{
    public static void SetMultiData(this NDArray arr, NDArray value, int[] indices)
    {
        foreach (var i in indices)
            arr.SetData(value, i);
    }
}