#573 – Array Covariance Doesn’t Apply to Value Types

Array covariance allows T[] to be assigned to U[], if can be assigned to U.

// Assignment compatibility, because Terrier is sub-type of Dog
Terrier t = new Terrier("Bob");
Dog d = t;

// Allowed because of array covariance
Terrier[] terriers = MakeTerrierArray();
Dog[] dogs = terriers;

This does not work, however, if the contents of the arrays are value types.  Arrays of value-typed objects are not covariant.

            byte b1 = 12;
            ushort u1 = b1;  // Assignment compatible

            byte[] bytearray = new byte[] { 1, 2, 3 };

            // Not allowed.  Compile-time error "Cannot implicitly convert type 'byte[]' to 'ushort[]'
            ushort[] shortarray = bytearray;

#280 – Implicitly-Typed Arrays

In the same way that you can declare an implicitly-typed variable, letting the compiler figure out the type of the variable’s value, you can declare implicitly-typed arrays.

In the example below, we declare and initialize both implicitly-typed variables and implicitly-typed arrays.  The compiler infers the type–shown in the associated comment.

            // Implicitly-typed variables
            var x = 42;         // int
            var s = "Hooey";    // string

            // Implicitly-typed arrays
            var someArray = new[] { 5, 6, 7 };             // int[]
            var arrB = new[] { new Dog("Bob"), null };     // Dog[]

#279 – Passing a Multidimensional Array to a Method

You can pass multi-dimensional arrays to methods.  Like arrays with a single dimension, the array is passed by value, with a copy of the reference passed to the method.  This means that the method can change elements in the array.

Here’s an example of a method that takes a two-dimensional array as an input parameter.

        public static void CountPawns(ChessPiece[,] chessboard, out int redPawns, out int blackPawns)
        {
            redPawns = 0;
            blackPawns = 0;

            for (int row = 0; row < 8; row++)
                for (int col = 0; col < 8; col++)
                {
                    ChessPiece piece = chessboard[row,col];
                    if (piece != null)
                    {
                        if (piece.PieceType == PieceTypeEnum.Pawn)
                        {
                            if (piece.PieceColor == PieceColorEnum.Red)
                                redPawns++;
                            else
                                blackPawns++;
                        }
                    }
                }
        }

When calling the method, you just pass the name of the array to the method.

            ChessPiece[,] board = new ChessPiece[8, 8];

            board[0, 1] = new ChessPiece(PieceTypeEnum.King, PieceColorEnum.Red);
            // continue to set up board here

            int redPawns;
            int blackPawns;
            Chess.CountPawns(board, out redPawns, out blackPawns);

#278 – Passing an Array by Reference

You can pass an array by reference to a method, using the ref or out parameter modifier, when you want the method to change the parameter to refer to a different array.

Here’s an example of a method that accepts an array and then overwrites the reference so that it points to a new array.

        public static void DoubleADogList(ref Dog[] list)
        {
            Dog[] bigList = new Dog[2 * list.Count()];

            int i2 = 0;
            foreach (Dog d in list)
            {
                bigList[i2++] = new Dog(d.Name, d.Age, d.Motto);
                bigList[i2++] = new Dog(d.Name + " Clone", d.Age, d.Motto);
            }

            // Change original reference to refer to new list
            list = bigList;
        }

We can call this method, passing it an array. When the method returns, the array variable points to the new (larger) array.

            Dog d1 = new Dog("Kirby", 13, "Chase balls");
            Dog d2 = new Dog("Jack", 17, "Bark");

            Dog[] list = new Dog[] { d1, d2 };

            Dog.DoubleADogList(ref list);

 

#155 – Iterating Through an Array Using the foreach Statement

You can write a loop–a block of code that is executed more than once–that executes once for each element in an array, using the C# foreach statement.

The foreach statement declares a variable local to the loop of the same type of the elements in the array.  This variable takes on the value of each element in the array.

            Person[] folks = new Person[4];
        	folks[0] = new Person("Bronte", "Emily");
	        folks[1] = new Person("Bronte", "Charlotte");
	        folks[2] = new Person("Tennyson", "Alfred");
	        folks[3] = new Person("Mailer", "Norman");

            string sLastNameList = "";

            // For each Person in array, dump out name and concatenate last name
            foreach (Person p in folks)
            {
                Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
                sLastNameList += p.LastName;
            }

#154 – Using an Invalid Array Index Causes an Exception to Be Thrown

If you try to reference an array element using an index that is outside the declared range of the array, an IndexOutOfRangeException exception will be thrown.

            int[] scores = { 88, 99, 79, 88};
            Console.WriteLine("5th element? - {0}", scores[4]);    // IndexOutOfRangeException

#153 – Returning a Subset of Array Elements Matching a Particular Criteria

Because System.Array implements the IEnumerable interface and because LINQ extends IEnumerable, you can use the IEnumerable.Where method on arrays to find a subset of elements that match a particular criteria.

The Where method accepts a delegate to a function that takes a single element of the same type as the array and returns a boolean value, indicating whether a match is found.  Where returns an IEnumerable collection, which can be iterated on to get the elements in the subset.

Here’s an example, finding the set of passing scores in a set of scores.

            int[] scores = { 89, 98, 72, 100, 68 };

            // Count number of passing grades
            int numPassed = scores.Where((Func<int,bool>)IsPassingGrade).Count();

Here’s the implementation of IsPassingGrade.

        static bool IsPassingGrade(int score)
        {
            return (score >= 75);
        }

You can avoid defining a separate function by using a lamba expression.

            int numPassed = scores.Where(s => s >= 75).Count();

Similar to Array.FindAll.

#152 – Remove Duplicate Array Entries Using Distinct() Method

You can use the IEnumerable.Distinct method on an array to get all of the elements of the array, with the duplicates removed.

This method returns a result of the type IEnumerable<T>, where T is the type of the element in the array.  You can iterate through this new list using the foreach statement, or count the number of elements using the Count method.

            int[] scores = { 88, 99, 79, 88, 78, 100, 79 };

            // # of unique scores
            Console.WriteLine("# unique = {0}", scores.Distinct().Count());   // 5

            // List only the unique scores
            foreach (int next in scores.Distinct())
                Console.WriteLine("Score : {0}", next);

#151 – Determining Whether an Array Contains a Specific Element

You can use the Array.Find method to find a particular element in an array.  If, however, you only care whether a particular element is present in an array, you can use the IEnumerable.Contains method.  The Contains method returns true if the element was found in the array, false if not.

            int[] scores = { 89, 98, 72, 100, 83 };

            bool someoneAcedIt = scores.Contains(100);   // true

Contains also works on arrays holding objects belonging to a custom type.

            Person[] folks = new Person[3];
            folks[0] = new Person("Bronte", "Emily", 29);
            folks[1] = new Person("Bronte", "Charlotte", 31);
            folks[2] = new Person("Tennyson", "Alfred", 24);

            bool foundAlfred = folks.Contains(new Person("Tennyson", "Alfred", 24));

This will only work as expected if the underlying type has implemented the equality operator in a way that properly compares two objects to determine if they are equal.  If the equality operator has not been defined, only the references to the objects will be compared, rather than the contents of the objects.

#150 – Other Aggregate Functions You Can Apply to Numeric Arrays

Post #147 mentioned using IEnumerable.Average to get the average of an array of numeric values.  The System.Linq namespace includes several other extension methods for IEnumerable that can be used to aggregate values:

  • Max – Get the maximum value
  • Min – Get the minimum value
  • Sum – Get the sum of all values

            int[] scores = { 89, 98, 72, 100, 83 };
            int sum = scores.Sum();        // 442
            int min = scores.Min();        // 72
            int max = scores.Max();        // 100
            double avg = scores.Average();       // 88.4

Follow

Get every new post delivered to your Inbox.

Join 43 other followers