#572 – Why Array Covariance Is Called Covariance

Array covariance in C# allows you to assign an array of instances of a derived class to a variable whose type is an array of instances of the base class.

Dog[] dogs = hounds;     // Where hounds is Hound[] and Hound is subclass of Dog

Covariance says that the ordering of two elements in a set is preserved after transforming each by the same function.

With array covariance, we can think of the “ordering” as being the fact that the subtype is narrower than the base class, which means that the assignment is allowed due to assignment compatibility.

The covariant function being applied to each type is to create an array of that type.  This “function”, an array of a type, is then covariant because if type T is narrower than type U, then T[] is also narrower than U[], preserving assignment compatibility.

#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[]

#277 – Passing an Array to a Method

A variable that is the name of an array is just a reference to an instance of System.Array.  Since it is a reference type, when you pass an array to a method, you pass a copy of the reference.  This means that the method has the ability to read and write elements of the array.

Here’s a method that accepts an array as an input parameter.  The code demonstrates that you can read from and write to elements of the array.

        public static void DumpMovieInfo(MovieInfo[] movieList)
        {
            for (int i = 0; i < movieList.Length; i++)
            {
                // Read element of the array
                Console.WriteLine("{0} ({1}), {2}", movieList[i].Title, movieList[i].Year, movieList[i].Director);

                // Rewrite element
                movieList[i].Title = movieList[i].Title.ToUpper();
                movieList[i].Director = movieList[i].Director.ToUpper();
            }
        }

You would call the method as follows:

            MovieInfo[] movies = { new MovieInfo("The African Queen", 1951, "John Huston"),
                                   new MovieInfo("Casablanca", 1942, "Michael Curtiz"),
                                   new MovieInfo("The Godfather", 1972, "Francis Ford Coppola")};

            Movie.DumpMovieInfo(movies);

            Movie.DumpMovieInfo(movies);

#148 – Getting the Average of an Array of Custom Objects

If you want to calculate the average (mean) of a collection of objects stored in an array, you can use the IEnumerable.Average method.

The Average method allows passing in an indication of the function that should be used to calculate the numeric value from each object, which will be used in generating the average.  This function takes an object whose type matches the type of the array as an input parameter and returns a numeric value, used in generating the average.  It will be called once for each element of the array.

You could define a static function and pass a delegate to that function.

        static int AgeOf(Person p)
        {
            return p.Age;
        }

        // Example of calling Average on Person[] array
        double avgAge = folks.Average((Func<Person,int>)AgeOf);

Alternatively, you could use a lambda expression, avoiding the need to define a separate function.

            double avgAge = folks.Average(person => person.Age);

#147 – Getting the Average of an Array of Numbers

If you have an array containing a collection of numeric values, you can get the average (mean) using the Average method.

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

            double avg = scores.Average();      // 88.4

The Average method is an extension method, applied to Array by virtue of its implementation of the IEnumerable interface.  It’s part of the System.Linq namespace, so you’ll need a using statement for this namespace.  Average is available in .NET 4 and 3.5.

Average will work for arrays containing any numeric type.

#140 – Making a Deep Copy of an Array Using an Object’s Clone Method

When you use the Array.Clone method of an array to make a copy of an array that contains instances of a reference type, you get a shallow copy–the new array references the same underlying objects as the original array.

A deep copy of an array is one in which you create a new copy of each element, so that the new array references different objects.

You can possibly make a deep copy if the element type has a Clone method.

            // Shallow copy of the array
            Person[] folks2 = (Person[])folks1.Clone();

            // Deep copy by calling Clone method on each element
            Person[] folks3 = new Person[folks1.Length];
            for (int i = 0; i < folks1.Length; i++)
                folks3[i] = (Person)folks1[i].Clone();

We’re now calling Clone on each element, rather than on the array itself.

This method doesn’t guarantee a deep copy, since there’s no guarantee that Person.Clone will give us a deep copy of Person.

#138 – Searching a Sorted Array

If an array is already sorted and if the type that its elements belong to has implemented IComparable, you can use the BinarySearch method to quickly search for a particular element.

Using our earlier example, with a Person class that implements IComparable (sorts by LastName/FirstName), we can search for a particular Person in a sorted 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");

            // Sort
            Array.Sort(folks);    // C. Bronte, E. Bronte, Mailer, Tennyson

            // Now search - returns index of 2 (3rd element)
            int normIndex = Array.BinarySearch(folks, new Person("Mailer", "Norman"));
            Person norm = folks[normIndex];     // Get reference to Normal

If you have a property not used for sorting purposes (e.g. Person.Age), you can’t be guaranteed that the object returned as a match actually matches your specified object–for that property.  Only properties used for comparison are guaranteed to match in the object returned.

Follow

Get every new post delivered to your Inbox.

Join 43 other followers