It should be written as a method which can sort an array of objects of any type.
How to
There are many different sorting methods, and here I will use a method that can be described as follows
• loop over the array and find the smallest element
• swap the smallest element with the element in position 0 – now the first item is correct
• loop over the last n-1 elements and find the smallest among these
• swap that element with the element in position 1 – now the first two elements are correct
• loop over the last n-2 elements and find the smallest among these
• swap that element with the element in position 2 – now the first three elements are in place
• continue now until the array is sorted – for each pass find the smallest of the elements that are not already in place and swap it to the right position
The result is that after k passes are the first k elements sorted while you still have to sort the last n-k elements. It is a very simple sorting method, but it is however not the most effective – at least not for large arrays.
Writing a method for sorting an array of any type, is a too large requirement, since a sorting of the elements will always include that the elements can be compared and ranked in order of size. Many of the built-in types can be, for example the simple types and the type string that can be compared with the comparison operators, but other types can also be compared, and it usually happens in that they implement an interface called IComparable. This interface defines only one method called CompareTo(), which has an object as a parameter. The protocol is that the method must return -1 if the current object is less than the parameter, 1 if the current object is greater than the parameter and otherwise 0. The interface is also available in a generic version and the method CompareTo() is thus also a generic parameterized with the kind of elements to be compared. The sum of all this is that the sorting method can be written as follows:
static void Sort<T>(T[] arr) where T : IComparable<T> {
for (int i = 0; i < arr.Length - 1; ++i) {
int k = i;
for (int j = i + 1; j < arr.Length; ++j) if (arr[j].CompareTo(arr[k]) < 0) k = j; if (i != k) Swap(ref arr[i], ref arr[k]); }
Download free eBooks at bookboon.com
C# 1 Introduction to programming and the C# language
161
Generic types
Explanation
The method is simple and expresses the above algorithm, but there are a few important things to note. Note first that it is a generic method parameterized with T, and that it has a parameter arr that is an array of the type T. Next, note you should note the where part that expresses that the parameter type T
must implement the interface IComparable<T> – thus a parameterized version of IComparable. Stated somewhat differently, the method can only work on arrays of types that implement this interface. If you try to apply the method to other types, you get a translation error. Note also how the method CompareTo()
is used to compare elements, and note finally how the generic method Swap() is used.
Test
Below is an example of how the method can be used to sort an array with elements of the type int:
static void Test1() { int[] t = { 17, 13, 29, 3, 19, 11, 5, 2, 7, 23 }; Print(t); Sort(t); Print(t); }
static void Print<T>(T[] arr) {
foreach (T t in arr) Console.Write("{0} ", t.ToString()); Console.WriteLine();
}
Note that also the method Print() is generic.
When things go well, this is due to the type int which implements the interface IComparable<int>. I will once again return to the type Dice, but this time with an extension so that it implements the
IComparable interface:
class Dice : IComparable<Dice> {
private static Random rand = new Random(); private int eyes;
public Dice() {
Throw(); }
public int Eyes {
get { return eyes; } }
public void Throw() {
eyes = rand.Next(1, 7); }
public override string ToString() {
return "" + eyes; }
public int CompareTo(Dice d) {
return eyes < d.eyes ? -1 : eyes > d.eyes ? 1 : 0; }
}
This means that the cubes can now be arranged as such a two is less than a three, etc. Note that how they are ranked, is something that the programmer has specified in the implementation of the method
CompareTo(), and that in principle one could have chosen any other arrangements.
As the next point I would like to create an array of cubes, but for this I will write a generic method that as a parameter has the size of the array:
Download free eBooks at bookboon.com
C# 1 Introduction to programming and the C# language
163
Generic types
static T[] Create<T>(int n) where T : new() {
T[] arr = new T[n];
for (int i = 0; i < arr.Length; ++i) arr[i] = new T(); return arr;
}
Note first the use of where. It means that the parameter type T must have a default constructor – if it is not the case, one gets a translation error. The result is that when the array elements are created in the loop, you can be sure that they are properly initialized. Note that the class Dice satisfies that and has a default constructor.
Below is a code that creates an array with cubes, and sort it:
static void Test2() { Dice[] b = Create<Dice>(10); Print(b); Sort(b); Print(b); }
Here you should note that the method Create() is generic and that the parameter does not depend on the parameter type. If you just write the Create(10), the translator can’t know what Create() you wish to perform, and one must therefore set the parameter type after the method’s name. In other examples, it is unnecessary (but legally) because the translator from the actual parameter can see what type it is. When, for example you write
Print(b);
the compiler can from the type of b to see what type the argument have, but it is legal to write
Print<Dice>(b);
Comment
In the examples above, I have shown two applications of the use of where to place restrictions on the type parameter. There are a few other cases:
• where T : struct
• where T: class
where the first means that the type parameter must be a value type, while the other means that the type should be a reference type. Finally I have in the method Sort() used that the type must implement an interface, but you can with the same syntax indicate that the type must inherit a class.