How to create a 1-Dimensional Array in C# with index starting at 1

18,763

Solution 1

All arrays in C# are zero based. As far as I know there is no way to create a 1 based array. Imagine what kind of a mess would have happened if that was possible. Here is a similar thread which explains the issue with more details - C#: Nonzero-based arrays are not CLS-compliant

Solution 2

You can make a non-zero-based array in C#, but the useage of it is kind-of obnoxious. It is definitly not a simple substitute for a normal (i.e., zero-based single dimentional) array.

        // Create the array.
        Array myArray = Array.CreateInstance(typeof(double), new int[1] { 12 }, new int[1] { 1 });

        // Fill the array with random values.
        Random rand = new Random();
        for (int index = myArray.GetLowerBound(0); index <= myArray.GetUpperBound(0); index++)
        {
            myArray.SetValue(rand.NextDouble(), index);
        }

        // Display the values.
        for (int index = myArray.GetLowerBound(0); index <= myArray.GetUpperBound(0); index++)
        {
            Console.WriteLine("myArray[{0}] = {1}", index, myArray.GetValue(index));
        }

The GetValue/SetValue syntax that is required for this is uglier than subtracting one from a vector index at each occurance.

If a value type is stored in the array, then it will be stored in consecutive position just as in a regular array, but the getter and setter will require boxing of the values (unless there is some compiler magic that I am not aware of). And the getter will usually require a cast (just to make it even uglier).

    double myValue = (double)myArray.GetValue(index);

Also note that the correct comparison for GetUpperBound is <=, unlike Length which is compared with <.

Solution 3

Non-Zero based arrays DO exist in C, and there IS a way to create a 1's (or whatever) based array.

I fully agree that they are messy, and they should not be used for anything other than legacy stuff, but they are ESSENTIAL to interact with old COM libraries.

The most common place to run into this is working with the Microsoft.Office.Interop.Excel.Range object in the Excel library which still uses the old DCOM interface underneath.

Example:

/// <summary>
    /// Makes the equivalent of a local Excel range that can be populated 
    ///  without leaving .net
    /// </summary>
    /// <param name="iRows">number of rows in the table</param>
    /// <param name="iCols">number of columns in the table</param>
    /// <returns>a 1's based, 2 dimensional object array which can put back to Excel in one DCOM call.</returns>
    public static object[,] NewObjectArray(int iRows, int iCols)
    {

        int[] aiLowerBounds = new int[] { 1, 1 };
        int[] aiLengths = new int[] { iRows, iCols};

        return (object[,])Array.CreateInstance(typeof(object), aiLengths, aiLowerBounds);

    }

In this case, the reason this code is necessary is each DCOM call to excel is a cross-process call, and if you were to access cells one-at-a-time, you'd incur huge overhead, (either retrieving or setting values). An Excel range is a 1's based 2 dimensional array, and if one creates the array, and populates it locally, it can be pushed to excel in one cross-process call, creating an enormous performance improvement.

Share:
18,763

Related videos on Youtube

Peter Stuer
Author by

Peter Stuer

Updated on May 29, 2022

Comments

  • Peter Stuer
    Peter Stuer almost 2 years

    For multidimensional arrays Array.CreateInstance can be used to create non-zero index based arrays, but if you try that for a 1-dimensional arrays (vectors) as in e.g.:

    public double[] myArray = (double[])Array.CreateInstance(typeof(double), new int[1] { 12 }, new int[1] { 1 });
    

    this will fail at run-time when the cast from the the multidimensional Array to a single-dimensional array fails

    "Unable to cast object of type 'System.Double[*]' to type 'System.Double[]'"
    

    Now I could just create a zero based array and ignore the first value, or work with offsets etc., but am I overlooking some c# syntactic magic that allows for non zero based vectors?

    Update:

    I'll take Eric Lippert's word for it if he says "There's no obvious way to make a non-zero-based array in C#"

    • Eric Lippert
      Eric Lippert about 13 years
      You could make a simple wrapper struct that wraps a zero-based array and has an indexer that does what you want -- adds or subtracts the right offset.
    • mike
      mike
      Hi, I disagree with Genady's answer (that you flagged as "Most Correct"). Could you please see the Answer I just added, and unflag Genady's answer, as it is not correct.
  • Brann
    Brann over 11 years
    actually, you can create 1-based arrays in c#, see topvoted answer below.
  • Stéphane Gourichon
    Stéphane Gourichon over 10 years
    Oh gosh, that's awful. I tried this in a LINQ context. The compiler accepts this particular constructor with two int[], but then the resulting Array object doesn't have any magical LINQ methods. (At least on .NET 4.) Thanks for sharing anyway.
  • Adrian Ratnapala
    Adrian Ratnapala almost 10 years
    @GenadySergeev I agree having two kinds of arrays would be a mess. But pascal-style arrays where the base index can be anything at all are actually quite useful and not messy. Funny how the world works.
  • mike
    mike over 8 years
    I down-voted this because it is a dangerously incorrect answer. Dot Net framework supports non-zero based arrays in order to maintain legacy compatibility with old COM/DCOM components. In particular the necessary local copy of an Excel.Range object requires a 1's based 2-dimensional array. Arrays can be dimensioned non-zero using Array.CreateInstance() docs here: msdn.microsoft.com/en-us/library/x836773a(v=vs.110).aspx
  • Martin Braun
    Martin Braun about 8 years
    In fact the COM Interop of Excel returns a non zero based multidimensional array when you access Worksheet.UsedRange.Value.
  • Garr Godfrey
    Garr Godfrey over 6 years
    COM arrays are not C# arrays.
  • CodeMonkey
    CodeMonkey over 5 years
    Thank you, Excel is exactly where I has this issue now.
  • Zverev Evgeniy
    Zverev Evgeniy about 5 years
    Utterly wrong statement. See the answer by Jeffrey L Whitledge.
  • Zverev Evgeniy
    Zverev Evgeniy about 5 years
    @StéphaneGourichon In order to use LINQ, you need to cast the Array type into IEnumerable<T>. LINQ is no magic. It is a mere set of type extensions.
  • NoChance
    NoChance over 3 years
    While the code works on my machine, when I use debug mode, locals, and expand the array name, I get "..." instead a list of indexed values as with arrays defined in the normal way....Strange!