Working with Arrays and Collections in C#

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Introduction

Typical programming constructs require the use of non-scalar data types.  In C#, the non-scalar types can come in two types: arrays and collections.

Arrays are a collection of objects of the same type. There is no limit to the number of elements you can store in arrays, however, arrays need to declare their size when they are created, unlike other collections where the size is dynamic.

The C# Basics

Arrays are of two types: single-dimensional arrays, and multi-dimensional arrays and jagged arrays.

Single-dimensional Arrays

Single-dimensional arrays can be thought of as list of items that span only a single line/file. Each element of the array can be accessed by an index that indicates the position of the element in the list.

Single dimensional arrays can be declared in one of three ways:

1.       Declaring an empty array by specifying the number of elements.

int[] array1 = new int[5]; 

2.       Declaring an array by initializing value of the elements.

int[] array1 = new int[] { 1, 2, 3, 4, 5 };

3.       Declaring an array but omitting the type when initializing the value of the elements.

int[] array1 = { 1, 2, 3, 4, 5 };

//Sample array declaration
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ArraysDemo
{
    class Program
    {
        static void Main(string[] args)
        {
 
 
            DeclareArray1();
            DeclareArray2();
            DeclareArray3();
        }
        static void DeclareArray1()
        {
            int[] array1 = new int[5];
        }
        static void DeclareArray2()
        {
            int[] array1 = new int[] { 1, 2, 3, 4, 5 };
        }
        static void DeclareArray3()
        {
            int[] array1 = { 1, 2, 3, 4, 5 };
        }
    }
}

The above listing shows the three different ways in which arrays can be declared/initialized.

If you compile the above code and open up an IL Disassembler like Ildasm, you can see the following:

//DeclareArray1()
.method private hidebysig static void  DeclareArray1() cil managed
{
  // Code size       9 (0x9)
  .maxstack  1
  .locals init ([0] int32[] array1)
  IL_0000:  nop
  IL_0001:  ldc.i4.5
  IL_0002:  newarr     [mscorlib]System.Int32
  IL_0007:  stloc.0
  IL_0008:  ret
} // end of method Program::DeclareArray1
 
 
//DeclareArray2()
.method private hidebysig static void  DeclareArray2() cil managed
{
  // Code size       20 (0x14)
  .maxstack  3
  .locals init ([0] int32[] array1)
  IL_0000:  nop
  IL_0001:  ldc.i4.5
  IL_0002:  newarr     [mscorlib]System.Int32
  IL_0007:  dup
  IL_0008:  ldtoken    field valuetype '<PrivateImplementationDetails>{026A1929-427B-4A6A-B438-AB91B4FBF9F3}'/'__StaticArrayInitTypeSize=20' '<PrivateImplementationDetails>{026A1929-427B-4A6A-B438-AB91B4FBF9F3}'::'$$method0x6000003-1'
  IL_000d:  call       void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array,
                                                                                                      valuetype [mscorlib]System.RuntimeFieldHandle)
  IL_0012:  stloc.0
  IL_0013:  ret
} // end of method Program::DeclareArray2
 
 
//DeclareArray3()
.method private hidebysig static void  DeclareArray3() cil managed
{
  // Code size       20 (0x14)
  .maxstack  3
  .locals init ([0] int32[] array1)
  IL_0000:  nop
  IL_0001:  ldc.i4.5
  IL_0002:  newarr     [mscorlib]System.Int32
  IL_0007:  dup
  IL_0008:  ldtoken    field valuetype '<PrivateImplementationDetails>{026A1929-427B-4A6A-B438-AB91B4FBF9F3}'/'__StaticArrayInitTypeSize=20' '<PrivateImplementationDetails>{026A1929-427B-4A6A-B438-AB91B4FBF9F3}'::'$$method0x6000004-1'
  IL_000d:  call       void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array,
                                                                                                      valuetype [mscorlib]System.RuntimeFieldHandle)
  IL_0012:  stloc.0
  IL_0013:  ret
} // end of method Program::DeclareArray3
 
 

From the IL disassembly above, you can see that at IL level, DeclareArray3() is the same as DeclareArray2() – hence, it is a matter of style how one can declare and initialize their array.

Multi-dimensional Arrays

Multi-dimensional arrays can be visualized as a grid (lists on two or more axes). A 2-dimensional array with 2 dimensions can be visualized as a square/rectangle, a 3-dimensional array as a cube, etc.

Multi-dimensional arrays can be declared similar to single-dimensional arrays.

// Listing 2
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ArraysDemo
{
    class Program
    {
        static void Main(string[] args)
        {
 
 
            DeclareArray1();
            DeclareArray2();
            DeclareArray3();
        }
        static void DeclareArray1()
        {
            int[] array1 = new int[5];
        }
        static void DeclareArray2()
        {
            int[] array1 = new int[] { 1, 2, 3, 4, 5 };
        }
        static void DeclareArray3()
        {
            int[] array1 = { 1, 2, 3, 4, 5 };
        }
 
        static void DeclareArray4()
        {
            int[,] array2 = new int[2,3];
        }
        static void DeclareArray5()
        {
            int[,] array2 = new int[2, 3]{{0,0,0},{1,1,1}};
        }
        static void DeclareArray6()
        {
            int[,] array2 = { { 0, 0, 0 }, { 1, 1, 1 } };
        }
 
    }
}

Consider the above example.

The IL disassembly for DeclareArray4() method:

.method private hidebysig static void  DeclareArray4() cil managed
{
  // Code size       10 (0xa)
  .maxstack  2
  .locals init ([0] int32[0...,0...] array2)
  IL_0000:  nop
  IL_0001:  ldc.i4.2
  IL_0002:  ldc.i4.3
  IL_0003:  newobj     instance void int32[0...,0...]::.ctor(int32,
                                                             int32)
  IL_0008:  stloc.0
  IL_0009:  ret
} // end of method Program::DeclareArray4
 
 

For DeclareArray5() and DeclareArray6(), the IL is similar.

//DeclareArray5()
.method private hidebysig static void  DeclareArray5() cil managed
{
  // Code size       21 (0x15)
  .maxstack  3
  .locals init ([0] int32[0...,0...] array2)
  IL_0000:  nop
  IL_0001:  ldc.i4.2
  IL_0002:  ldc.i4.3
  IL_0003:  newobj     instance void int32[0...,0...]::.ctor(int32,
                                                             int32)
  IL_0008:  dup
  IL_0009:  ldtoken    field valuetype '<PrivateImplementationDetails>{42358043-DC76-4729-A8BB-15C791FC9B92}'/'__StaticArrayInitTypeSize=24' '<PrivateImplementationDetails>{42358043-DC76-4729-A8BB-15C791FC9B92}'::'$$method0x6000006-1'
  IL_000e:  call       void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array,
                                                                                                      valuetype [mscorlib]System.RuntimeFieldHandle)
  IL_0013:  stloc.0
  IL_0014:  ret
} // end of method Program::DeclareArray5
 
 
//DeclareArray6()
.method private hidebysig static void  DeclareArray6() cil managed
{
  // Code size       21 (0x15)
  .maxstack  3
  .locals init ([0] int32[0...,0...] array2)
  IL_0000:  nop
  IL_0001:  ldc.i4.2
  IL_0002:  ldc.i4.3
  IL_0003:  newobj     instance void int32[0...,0...]::.ctor(int32,
                                                             int32)
  IL_0008:  dup
  IL_0009:  ldtoken    field valuetype '<PrivateImplementationDetails>{42358043-DC76-4729-A8BB-15C791FC9B92}'/'__StaticArrayInitTypeSize=24' '<PrivateImplementationDetails>{42358043-DC76-4729-A8BB-15C791FC9B92}'::'$$method0x6000007-1'
  IL_000e:  call       void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array,
                                                                                                      valuetype [mscorlib]System.RuntimeFieldHandle)
  IL_0013:  stloc.0
  IL_0014:  ret
} // end of method Program::DeclareArray6
 
 

Jagged Arrays

Jagged arrays are arrays of arrays, with each array potentially of a different size.

Jagged arrays are declared as under:

int[][] myJaggedarray = new int [10][];

The IL for the above statement within a method DeclareArray7() is shown below:

.method private hidebysig static void  DeclareArray7() cil managed
{
  // Code size       10 (0xa)
  .maxstack  1
  .locals init ([0] int32[][] myJaggedArray)
  IL_0000:  nop
  IL_0001:  ldc.i4.s   10
  IL_0003:  newarr     int32[]
  IL_0008:  stloc.0
  IL_0009:  ret
} // end of method Program::DeclareArray7

Arrays and Foreach Statement

Arrays can be iterated over using the foreach statement.

foreach(int i in myArray)
{
  // Do something.
 }

Collections

Other non-scalar types supported by .NET are the collections type, which are homed in the System.Collections namespace. The various types supported by the System.Collections namespace includes list, map, tree and stack. These collections are generic, i.e. they can support types that are provided when the class is defined.

Collections can be created by calling the appropriate constructor for the collection type.

For e.g., List of strings can be declared as shown below.

System.Collections.Generic.List<string> myList = new System.Collections.Generic.List<string>();

The IL Disassembly for the above statement when it is part of DeclareListCollection() method is:

.method private hidebysig static void  DeclareListCollection() cil managed
{
  // Code size       8 (0x8)
  .maxstack  1
  .locals init ([0] class [mscorlib]System.Collections.Generic.List`1<string> stringList)
  IL_0000:  nop
  IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ret
} // end of method Program::DeclareListCollection
 
 

The main difference between arrays and collections is that the size of collections is not determined when it is declared. It is dynamic.

When to Use Collections Over Arrays

You will typically be using collections over arrays because of the flexibility generics over and the types supported in the System.Collections namespace.

Arrays are preferred when the values need to be referenced by indexes.

Summary

In this article, we learned the basics of working with arrays and collections in C#. I hope you have found this information useful.

About the author

Vipul Patel is a Program Manager currently working at Amazon Corporation. He has formerly worked at Microsoft in the Lync team and in the .NET team (in the Base Class libraries and the Debugging and Profiling team). He can be reached at vipul.patel@hotmail.com

References

http://msdn.microsoft.com/en-us/library/9ct4ey7x%28v=vs.90%29.aspx

http://msdn.microsoft.com/en-us/library/9b9dty7d%28v=vs.120%29.aspx

http://msdn.microsoft.com/en-us/library/ybcx56wz%28v=vs.90%29.aspx

http://www.bing.com/search?q=C%23+arrays+and+collections&pc=MOZI&form=MOZSBR

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read