简体   繁体   中英

Convert Excel Range to C# Array

I would like to convert an Excel Range to a C# Array with this code:

System.Array MyRange = (System.Array)range.cells.value;

for (int k = 0; k <= MyRange.Length; k++)
{
    List<service_name> _ml = new List<service_name>();
    for (int j = 1; j < dataitems.Count; j++)
    {
        // enter code here
    }
}

And then iterate over it like in the above loop.

But this code does not work, and throws this Exception:

"Unable to cast object of type 'System.String' to type 'System.Array'."

Based on the help provided my Microsoft here , this is how I read and write an array in Excel.

var xlApp=new Microsoft.Office.Interop.Excel.Application();
var wb=xlApp.Workbooks.Open(fn, ReadOnly: false);
xlApp.Visible=true;
var ws=wb.Worksheets[1] as Worksheet;
var r=ws.Range["A2"].Resize[100, 1];
var array=r.Value;
// array is object[1..100,1..1]
for(int i=1; i<=100; i++)
{
    var text=array[i, 1] as string;
    Debug.Print(text);
}
// to create an [1..100,1..1] array use
var array2=Array.CreateInstance(
    typeof(object), 
    new int[] {100, 1}, 
    new int[] {1, 1}) as object[,];

// fill array2
for(int i=1; i<=100; i++)
{
    array2[i, 1] = string.Format("Text{0}",i);
}
r.Value2=array2;

wb.Close(SaveChanges: true);
xlApp.Quit();

调试高强

The error message in the original post occurs when the range consists of exactly one cell, because the resulting value's type is variant, and actually can be array, double, string, date, and null.

One solution can be that you check the cell count and act differently in case of exactly one cell.

My solution creates an array of cells. This works even if one or more cells are empty, which could causes a null object. (When all cells of the range are empty, Range.Cells.Value is null.)

System.Array cellArray = range.Cells.Cast<Excel.Range>().ToArray<Excel.Range>();

If you prefer Lists over Arrays (like I do), you can use this code:

List<Excel.Range> listOfCells = range.Cells.Cast<Excel.Range>().ToList<Excel.Range>();

Range can be one- or two-dimensional.

Finally, if you definitely need a string array, here it is:

string[] strArray = range.Cells.Cast<Excel.Range>().Select(Selector).ToArray<string>();

where Selector function looks like this:

public string Selector(Excel.Range cell)
{
    if (cell.Value2 == null)
        return "";
    if (cell.Value2.GetType().ToString() == "System.Double")
        return ((double)cell.Value2).ToString();
    else if (cell.Value2.GetType().ToString() == "System.String")
        return ((string)cell.Value2);
    else if (cell.Value2.GetType().ToString() == "System.Boolean")
        return ((bool)cell.Value2).ToString();
    else
        return "unknown";
}

I included "unknown" for the case I don't remember all data types that Value2 can return. Please improve this function if you find any other type(s).

The error means that the value is a string, so you can't convert it directly to array.

If the value is for example comma delimeted string you can use Split to get an array:

string[] MyRange = (range.Cells.Value + "").Split(',');
for (int k = 0; k < MyRange.Length; k++)
{
    //...loop here...
}

Also fixed your loop, you were going to get Index Out of Bounds error.

Late to the conversation, but here is a method to do this:

static string[][] GetStringArray(Object rangeValues)
{
    string[][] stringArray = null;

    Array array = rangeValues as Array;
    if (null != array)
    {
        int rank = array.Rank;
        if (rank > 1)
        {
            int rowCount = array.GetLength(0);
            int columnCount = array.GetUpperBound(1);

            stringArray = new string[rowCount][];

            for (int index = 0; index < rowCount; index++)
            {
                stringArray[index] = new string[columnCount-1];

                for (int index2 = 0; index2 < columnCount; index2++)
                {
                    Object obj = array.GetValue(index + 1, index2 + 1);
                    if (null != obj)
                    {
                        string value = obj.ToString();

                        stringArray[index][index2] = value;
                    }
                }
            }
        }
    }

    return stringArray;
}

called with:

string[][] rows = GetStringArray(range.Cells.Value2);

NOTE: This example only works with a range that is MORE THAN ONE CELL. If the Range is only a single cell (1x1), Excel will treat it in a special way, and the range.Value2 will NOT return a 2-dimensional array, but instead will be a single value. It's these types of special cases that will drive you nuts, as well as zero and non-zero array lower bounds:

using Excel = Microsoft.Office.Interop.Excel;

private static void Test()
{
    Excel.Range range = Application.ActiveWorkbook.ActiveSheet.Range["A1:B2"]; // 2x2 array

    range.Cells[1, 2] = "Foo"; // Sets Cell A2 to "Foo"
    dynamic[,] excelArray = range.Value2 as dynamic[,]; // This is a very fast operation
    Console.Out.WriteLine(excelArray[1, 2]); // => Foo

    excelArray[1, 2] = "Bar";
    range.Value2 = excelArray; // Sets Cell A2 to "Bar", again a fast operation even for large arrays
    Console.Out.WriteLine(range.Cells[1, 2]); // => Bar

Note that excelArray will have row and column lower bounds of 1:

    Console.Out.WriteLine("RowLB: " + excelArray.GetLowerBound(0)); // => RowLB: 1
    Console.Out.WriteLine("ColLB: " + excelArray.GetLowerBound(1)); // => ColLB: 1

BUT, if you declare a newArray in C# and assign it, then the lower bounds will be 0, but it will still work:

    dynamic[,] newArray = new dynamic[2, 2]; // Same dimensions as "A1:B2" (2x2)
    newArray[0, 1] = "Foobar";
    range.Value2 = newArray; // Sets Cell A2 to "Foobar"
    Console.Out.WriteLine(range.Cells[1, 2]); // => Foobar

Fetching this value out of the range will give you the original array with lower bounds of 0:

    range.Cells[1, 2] = "Fubar";

    dynamic[,] lastArray = range.Value2 as dynamic[,];
    Console.Out.WriteLine(lastArray[0, 1]); // => Fubar

    Console.Out.WriteLine("RowLB: " + lastArray.GetLowerBound(0)); // => RowLB: 0
    Console.Out.WriteLine("ColLB: " + lastArray.GetLowerBound(1)); // => ColLB: 0
}

Working with Excel Interop can be daunting as there are many special cases like this in the codebase, but I hope this helps clarify at least this one.

The issue is because ,when your range becomes a single cell ,and "range.value"/"range.cell.value" means String value,this string cannot be put into object array. So check if your range has only one cell or more and do according to that

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM