如何快速将 object[,] 向上转换为 double[,]?

问题描述 投票:0回答:6

我使用

Microsoft.Office.Interop.Excel
我返回了一个
object[,]
类型的二维数组,其中包含
double
元素。请注意,索引下限是
1
,而不是默认的
0
,但我可以轻松处理。

如何使用 .NET 3.5 nicely 将数组转换为

double[,]
。 (很好,我的意思是简洁或紧凑)。

请注意

double[] values_2 = values.Cast<double>().ToArray();

确实有效,但它通过数组展平为一维结构。

c# vb.net office-interop
6个回答
33
投票
object[,] src = new object[2, 3];

// Initialize src with test doubles.
src[0, 0] = 1.0;
src[0, 1] = 2.0;
src[0, 2] = 3.0;
src[1, 0] = 4.0;
src[1, 1] = 5.0;
src[1, 2] = 6.0;

double[,] dst = new double[src.GetLength(0), src.GetLength(1)];
Array.Copy(src, dst, src.Length);

3
投票

我不会说有一种方法比另一种方法更快,只要你不做任何愚蠢的事情。我想说的是,如果可以的话,在访问它们时就投射它们,而不是预先投射它们。当然,这取决于您打算如何访问它们。如果您要多次对数组进行索引,那么拆箱的成本可能会开始变得过高。如果您只扫描一次数组,那么就可以随心所欲地进行投射。


3
投票

这应该在大多数情况下有效,但如果您不分配转换委托,可能会引发异常。

public static TResult[,] Convert<TSource, TResult>(
    this TSource[,] array, Func<TSource, TResult> conversion = null) {

        if(array == null) throw new ArgumentNullException("array");

        if (conversion == null) {
            var resultType = typeof(TResult);
            conversion = source => (TResult)System.Convert.ChangeType(source, resultType);
        }

        var width = array.GetLength(1);
        var height = array.GetLength(0);
        var result = new TResult[height, width]; 

        for (int i = 0; i < height; ++i)
            for (int j = 0; j < width; ++j)
                result[i, j] = conversion(array[i, j]);

        return result;
    }

2
投票
Array.Copy(src, dst, src.Length);

如果

src 中的任何 value 为空,此代码将会出错。

由于在上面的代码中src有一个定义的值,它工作得很好。

如果 src 的值是动态设置的,不幸的是,如果其中任何一个值为 null,则上述代码将不起作用,因为

value
将无法成功复制。


1
投票

这里有几个问题。

首先,由于

double
不是引用类型,因此必须装箱才能存储在 object[] 中,因此获取值的唯一方法是将值拆箱到 double[] (复制副本)中。

另一个问题是,在 C# 中,数组是协变的,但不是逆变的,您可以将数组分配给派生程度较高的类型的引用,而不是派生程度较低的类型的引用。

string[] strings = new string[10];
object[] objects = strings; // OK, covariant
string[] strings2 = objects; // not OK, contravariant
objects[0] = 10; // Compiles fine, runtime ArrayTypeMismatchException!

1
投票

只是想添加一个关于通过互操作从 Excel 读取数组的有用想法。我总是使用自定义类(下面的代码)来强制 Excel 交出看起来像基数为零的数组,以防止我的更广泛的代码在基数之间混淆。

在下面的示例中,我实际上使用了两个构造函数来捕获从 Excel 读取的单单元格范围的特殊情况,因为 Excel 很烦人并且在这种情况下不会移交数组。这样,ExcelData 类就会创建一个 1×1 数组,这样当遇到单个单元格时,其余代码就不会中断!

public class ExcelData
{   // This class wraps an Excel range read - it will either be created by:
    // 1. An object[,] base-1 array from Excel stored as excelData[1..rows,1..cols]
    // 2. An object scalar value from Excel stored as _excelData[0,0]
    // In either case, the 'this' identifier allows access as if it was a base-0 array.
    private object[,] _excelData;
    private bool _baseZero;
    public ExcelData(object[,] excelData)
    { // Constructor for usual case of receiving a base-1 object[,] from Excel
        this._excelData = excelData;
        this._baseZero = false;
    }
    public ExcelData(object excelData)
    { // Constructor for special case where only single value is returned
        this._excelData = new object[1,1];
        this._excelData[0, 0] = excelData;
        this._baseZero = true;
    }
    public object this[int x, int y]
    { // Returns the correct element on a zero-basis
        get
        {
            if (this._baseZero)
            { return this._excelData[x, y]; }
            else
            { return this._excelData[x + 1, y + 1]; }
        }
        set
        {
            if (this._baseZero)
            { this._excelData[x, y] = value; }
            else
            { this._excelData[x + 1, y + 1] = value; }
        }
    }
    public int RowCount { get { return _excelData.GetLength(0); } }
    public int ColCount { get { return _excelData.GetLength(1); } }
}

当然,如果您愿意,您可以修改上面的内容以显式添加属性来读取特定类型的数据。这样,您可以将数据存储为对象 [*,*],就像 Excel 为您提供(并期望返回)一样,但在使用 Get/Set 方法检索(或更新)数据时将其视为不同类型的数组。

© www.soinside.com 2019 - 2024. All rights reserved.