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

发布于 2024-10-18 20:42:05 字数 362 浏览 3 评论 0原文

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

如何使用 .NET 3.5 很好地将数组转换为 double[,]。 (很好,我的意思是简洁或紧凑)。

请注意,这

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

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

I using Microsoft.Office.Interop.Excel I get returned a 2D array of type object[,] which contains double for elements. Note that the index lower bound is 1 instead of the default 0, but I can deal with that easily.

How can nicely convert the array into double[,] using .NET 3.5. (by nicely I mean concise, or compact).

Note that

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

does work, but it flattens by array into a 1D structure.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

静谧 2024-10-25 20:42:05
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);
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);
如果没有你 2024-10-25 20:42:05

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

I wouldn't say that there is one way that's faster than another so long as you don't do anything stupid. I'd say that, if you can, cast them when you access them rather than up front. Of course this depends on how you intend to access them. If you're going to index into the array more than once then the cost of unboxing might start to get too much. If you're only scanning the array once, then cast as you go.

筱武穆 2024-10-25 20:42:05

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

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;
    }

This should work in most cases, but may throw an exception if you don't assign a conversion delegate.

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;
    }
錯遇了你 2024-10-25 20:42:05
Array.Copy(src, dst, src.Length);

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

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

如果 src 的值是动态设置的,并且不幸的是,如果任何值为 null,则上述代码将不起作用,因为 value 不会即可复制成功。

Array.Copy(src, dst, src.Length);

This code will get an error if any of the value in src is null.

Since in the above code src has a defined value it works fine.

If the value of src is dynamically set, and unfortunately if any of the value is null, the above code will not work because the value wont be copied successfully.

往事随风而去 2024-10-25 20:42:05

这里有几个问题。

首先,由于 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!

There are a couple problems here.

First, since double is not a reference type it must be boxed to be stored in an object[], so the only way to get to the values is to unbox the values into a double[] (copy copy).

The other problem is that in C#, arrays are covariant but not contravariant, you can assign an array to a reference of a more-derived type, not not to one of a less-derived type.

string[] strings = new string[10];
object[] objects = strings; // OK, covariant
string[] strings2 = objects; // not OK, contravariant
objects[0] = 10; // Compiles fine, runtime ArrayTypeMismatchException!
无远思近则忧 2024-10-25 20:42:05

只是想添加一个关于通过互操作从 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 方法检索(或更新)数据时将其视为不同类型的数组。

Just wanted to add a helpful thought about reading arrays in from Excel via interop. I always use a custom class (code below) to force Excel to hand over what looks like a base-zero array, to keep my wider code from getting confused between bases.

In my example below, I actually use two constructors, to catch the special case where it is a single-cell Range being read in from Excel, because Excel is annoying and does not hand over an array in this case. This way, the ExcelData class creates a 1-by-1 array so the rest of your code can not break when a single cell is encountered!

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); } }
}

You could, of course, modify the above to explicitly add Properties to read out data in a specific Type if you wished. That way you can store the data as an object[*,*] like Excel gives you (and expects back) but treat it like a different kind of array when retrieving (or updating) data using Get/Set methods.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文