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:
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