Click here to Skip to main content
Click here to Skip to main content

Non-Zero-Based Arrays and Interface

By , 16 Mar 2009
Rate this:
Please Sign up or sign in to vote.

Introduction

Arrays represent a fixed number of elements of a particular type and thus allow you to treat several items as a single collection. Arrays in .NET are mostly zero-based and all array types are implicitly derived from the System.Array abstract class, which itself is derived from the System.Object root class. This means that arrays are always reference types that are allocated on the managed heap (as opposed to inline on the thread stack as a sequence of bytes) and that your application’s variable or field contains a reference to the array and not the elements of the array itself. This article will focus on the use of non-zero-lower bound arrays. Prior to that subject, it is necessary to touch on a few basics of System.Array. To declare and sort an array:

using System;
class Test {
 static void Main() {
 int[]  ar = { 3, 1, 2 };   	// declare and initialize an array
Array.Sort(ar);           		// call a shared/static method
Console.WriteLine("{0},  {1}, {2}",  ar[0], ar[1], ar[2]);  //display the result
   }
}

OUTPUT: 3, 1, 2

The reader will note the simplicity of this code and many wonder how it can be used for practical purposes. That will come later. In C# ( and the CLR), an array is denoted with square brackets after the element type. For example: char[] vowels = new char[5]; Square brackets also index the array, accessing a particular element:

char[] vowels = new char [5];
 vowels [0] = 'A';
 vowels [1] = 'E';
 vowels [2] = 'I';
 vowels [3] = 'O';
 vowels [4] = 'U';
Console.WriteLine([i]);   // where the result is “E”.

The for loop is one control structure that steps over every element in an array and will therefore cycle the integer I from 0 to 4:

using System;
class Program {
static void Main() {
 char[] vowels = new char [5];
 vowels [0] = 'A';
 vowels [1] = 'E';
 vowels [2] = 'I';
 vowels [3] = 'O';
 vowels [4] = 'U';

for (int i = 0; i < vowels.Length; i++)
 {
   Console.Write( vowels [i]);
        }
    }
}

OUTPUT: A, E, I, O, U

The length property of an array returns the number of elements in an array, but these arrays do not seem to do much instead print an output of a string of alphanumeric characters in the form an array.

The CLR permits you to create and work with arrays that have non-zero-lower bounds. To create a dynamic array, you can call the System.Array’s class static CreateInstance( ) method. Several overloads of this method exist, allowing you to specify the type of elements in the array, the number of dimensions in the array, the lower bounds of each dimension, and the number of elements in each dimension. Mathematically, a point on a line segment is one dimensional. The line segment is two dimensional, and the geometric circle or square are three dimensional. CreateInstance allocates memory for the array, saves the parameter information in the overhead’s portion of the array’s memory block, and returns a reference to the array. Here is some code that illustrates how to dynamically create a 2-dimensional array of System.Decimal values. The first dimension represents calendar years from 2008 to 2012. While we have not reached the year 2010 at the writing of this article, the use of the array is to show if the income is growing (ideally when the company is expanding: a hypothetical scenario). The second dimension represent fiscal quarters from 1 to 4 inclusive.

Non-Zero-Lower Bound Arrays

using System;
public sealed class Program {
 public static void Main() {
 Int32[] lowerBounds = { 2008, 1 };
 Int32[] lengths     = {   5,   4};
 Decimal[,] quarterlyIncome = (Decimal[,])
  Array.CreateInstance(typeof(Decimal), lengths, lowerBounds);
  Console.WriteLine("{0,4}  {1, 9}  {2, 9} {3, 9}, {4, 9}", 
				"Year", "Q1", "Q2", "Q3" , "Q4");
  Int32 firstYear =  quarterlyIncome.GetLowerBound(0);
  Int32 lastYear =   quarterlyIncome.GetUpperBound(0);
  Int32 firstQuarter =  quarterlyIncome.GetLowerBound(1);
   Int32 lastQuarter = quarterlyIncome.GetUpperBound(1);

   for (Int32 year = firstYear; year <= lastYear; year++) {
  Console.Write(year + " ");
   for (Int32 quarter = firstQuarter; quarter <= lastQuarter; quarter++) {
  Console.Write("{0,9:c}  ",  quarterlyIncome[year, quarter]);
   }
  Console.WriteLine();
   }
       }
 } 

1.JPG

Array Access Performance

As stated earlier, arrays are a way to treat several items (elements) as a single collection. Collections start off where arrays end and it is the hope of the writer of this paper that the beginner can move up to Collections and Generics. But recall that a reference to the array itself is stored on the managed heap, and not the elements. Therefore accessing items from an array would be a measure of performance. Internally, the CLR actually supports two different kinds of arrays:

  • Single-dimensional arrays with a lower-bound of 0
  • Single-dimensional and multi-dimensional arrays with unknown lower-bound

The code below will first demonstrate that when an array has two or more dimensions, you can cast the reference returned from CreateInstance( ) to an ElementType[ ] variable  (where ElementType is some type name), making it easier for you to access the elements in an array. If the array has one dimension, in C#, you have to use the Array class’s methods GetValue and SetValue to access the elements of an array.

     using System;
     public static class Program {
     public static void Main() {
    Array a;
         a = new String[0];
    Console.WriteLine(a.GetType());    // System.String[]
         a = Array.CreateInstance(typeof(String),
             new Int32[] { 0 }, new Int32[] { 0 });
    Console.WriteLine(a.GetType());    // System.String[]
         a = Array.CreateInstance(typeof(String),
             new Int32[] { 0 }, new Int32[] { 1 });
    Console.WriteLine(a.GetType());    // System.String[*]  
      Console.WriteLine();
                             
     a = new String[0, 0];
     Console.WriteLine(a.GetType());    // System.String[,]
     a = Array.CreateInstance(typeof(String), new Int32[] { 0, 0 }, new Int32[]
                 { 0, 0 });
        Console.WriteLine(a.GetType());    // System.String[,]
                              a = Array.CreateInstance(typeof(String),
                               new Int32[] { 0, 0 },
                               new Int32[] { 1, 1 });
        Console.WriteLine(a.GetType());    // System.String[,]
    }
}

2.JPG

Notice the comments that follow each Console.WriteLine indicating an output. For a single-dimension zero-based array displays a type name of System.String[]. The one-based array displays a type of System.String[*]. The * lets the CLR know that this is not a zero-based array. For multidimensional arrays, the Console.WriteLine displays a type of System.String[,]. It should be obvious that accessing the elements of a single-dimensional, zero-based array is quicker than accessing the elements of non-zero-based arrays. The reason for this can be found in the metadata and IL code that is emitted by the CSC.exe compiler. When typing the command ildasm /metadata=validate /out: Access.il Access.il to the type ‘type Access.il’,you can examine and research the IL code. Jeffrey Richter, an expert on the CLR (parts of this paper have been referenced from his book “The CLR via C#”), has insisted that the C# compiler can be deceiving. During compilation there are unnecessary objects created and the code must be optimized using the /optimize+ switch. Mr. Richter has further stated that his tool of choice is the ildasm.exe. Below is a snippet of the IL code (avoiding the metadata tables):

{
    .entrypoint
    // Code size       62 (0x3e)
    .maxstack  5
    .locals init (int32[] V_0)
    IL_0000:  nop
    IL_0001:  ldc.i4.3
    IL_0002:  newarr     [mscorlib]System.Int32 //NOTE
    IL_0007:  dup
    IL_0008:  ldtoken    field valuetype '<privateimplementationdetails>{AD4A1C2
5-8CEB-439C-88EC-E7E4AD0D78E3}'/'__StaticArrayInitTypeSize=12'
 '<privateimplemen>{AD4A1C25-8CEB-439C-88EC-E7E4AD0D78E3}'::'$$method0x6000001-1'
    IL_000d:  call       void [mscorlib]System.Runtime.CompilerServices.RuntimeH
elpers::InitializeArray(class [mscorlib]System.Array,

                        valuetype [mscorlib]System.RuntimeFieldHandle)
    IL_0012:  stloc.0
    IL_0013:  ldloc.0
    IL_0014:  call       void [mscorlib]System.Array::Sort<int32>(!!0[])
    IL_0019:  nop
    IL_001a:  ldstr      "{0},  {1}, {2}"
    IL_001f:  ldloc.0
    IL_0020:  ldc.i4.0
    IL_0021:  ldelem.i4   //NOTE
    IL_0022:  box        [mscorlib]System.Int32
    IL_0027:  ldloc.0
    IL_0028:  ldc.i4.1
    IL_0029:  ldelem.i4  //NOTE
    IL_002a:  box        [mscorlib]System.Int32
    IL_002f:  ldloc.0
    IL_0030:  ldc.i4.2
    IL_0031:  ldelem.i4
    IL_0032:  box        [mscorlib]System.Int32
    IL_0037:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                  object,
                                                                  object,
                                                                  object)
    IL_003c:  nop
    IL_003d:  ret
  } // end of method Test::Main

There are specific IL instructions, like newarr, ldelem, ldelema, ldlen, and stelen, which are used to manipulate single dimension, zero-based arrays. These special IL instructions cause the JIT compiler to emit optimized code.

Interfaces

Anyone who has ever practiced COM programming will be familiar with the interface. In COM, an interface is pointed at when there is an attempt to access a COM component. The interface is actually just that: an interface between groups of semantically-related functions that are pointed in the “virtual “table” by “virtual pointers”. But in C++, inheriting a class is not always the same as inheriting an interface. In the Microsoft .NET Framework, there is a class System.Object that defines four instance public methods: ToString(), Equals(), GetHashCode(), and GetType(). Consider the famous “Hello, World!” code.

using System
class App {
static void Main() {
Console.WriteLine(“Hello, World!”);
  }
}

Hello, World!

Now we run a program that loads this managed module and uses the Reflection API to emit what we discussed above:

using System;
using System.IO;
using System.Reflection;
public class Meta {
 public static int Main() {
 Assembly a = Assembly.LoadFrom("hello.exe");
 Module[] m = a.GetModules();
 Type[]  types = m[0].GetTypes();
 Type type = types[0];
 Console.WriteLine("Type [{0}] has these methods:", type.Name);
 MethodInfo[] mInfo = type.GetMethods();
 foreach (MethodInfo mi in mInfo)
 {
  Console.WriteLine("  {0}", mi);
  }
 return 0;
      }
}

We compile this code, having loaded the assembly in the Assembly class’s LoadFrom method:

C:\Windows\Microsoft.NET\Framework\v2.0.50727>csc /optimize+ meta.cs 
c:\Windows\Microsoft.NET\Framework\v2.0.50727>meta
Type [App] has these methods:
  System.String ToString()
  Boolean Equals(System.Object)
  Int32 GetHashCode()
  System.Type GetType()

Since the System.Object namespace is the root class, all other classes inherit those four public instance methods. Actually, any class derived from Object inherits the following:

  • The method signatures
  • The implementation of these methods

An interface is just another way to define a named set of method signatures. An interface can inherit other interfaces. For example, the ICollection<t> interface must implement all of the methods defined by ICollection<t>, IEnumerable<t>, and IEnumberable interfaces.

Define a Type that Implements an Interface

The System.IComparable<t> interface is defined in Mscorlib.dll as follows:

public interface IComparable<t> {
Int32 CompareTo( T other);
}

The code shown below demonstrates how to define a type that implements this interface and also shows code that compares two object Points:

using System;

// Point is derived from System.Object and implements IComparable<t>.
public sealed class Point : IComparable<point> {
   private Int32 m_x, m_y;

   public Point(Int32 x, Int32 y) {
      m_x = x;
      m_y = y;
   }

   // This method implements IComparable<t> for Point
    public Int32 CompareTo(Point other) {
      return Math.Sign(Math.Sqrt(m_x * m_x + m_y * m_y)
         - Math.Sqrt(other.m_x * other.m_x + other.m_y * other.m_y));
   }

   public override String ToString() {
      return String.Format("({0}, {1})", m_x, m_y);
   }
}

public static class Program {
   public static void Main() {
      Point[] points = new Point[] {
         new Point(3, 3),
         new Point(1, 2),
      };

      if (points[0].CompareTo(points[1]) > 0) {
         Point tempPoint = points[0];
         points[0] = points[1];
         points[1] = tempPoint;
      }
      Console.WriteLine("Points from closest to (0, 0) to farthest:");
      foreach (Point p in points)
         Console.WriteLine(p);
   }
}

OUTPUT

Points from closest to (0, 0) to farthest:
(1, 2)
(3, 3)

References

  • The CLR via C#, 2nd Edition, by Jeffrey Richter

History

  • 16th March, 2009: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

logicchild
Other Pref. Trust
United States United States
I started electronics training at age 33. I began studying microprocessor technology in an RF communications oriented program. I am 43 years old now. I have studied C code, opcode (mainly x86 and AT+T) for around 3 years in order to learn how to recognize viral code and the use of procedural languages. I am currently learning C# and the other virtual runtime system languages. I guess I started with the egg rather than the chicken. My past work would indicate that my primary strength is in applied mathematics.

Comments and Discussions

 
GeneralMathematics terminology error Pinmemberozbear23-Mar-09 14:12 
GeneralPerformance PinmemberMichal Brylka16-Mar-09 21:58 
GeneralRe: Performance PinmemberHenry Minute17-Mar-09 2:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140415.2 | Last Updated 17 Mar 2009
Article Copyright 2009 by logicchild
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid