Exercise 1:: ('A', 'B','C') LST - Add ('D') LST - Add ('E')
Exercise 1:: ('A', 'B','C') LST - Add ('D') LST - Add ('E')
using System;
using System.Collections.ObjectModel; using System.Collections.Generic;
}
catch(Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
Exercise 2:
Let us now assume that we wish to make our own, specialized (non-generic) collection class of a particular type of
objects. Below we will - for illustrative purposes - write a class called AnimalFarm which is intended to hold
instances of class Animal. It is reasonable to program AnimalFarm as a subclass of an existing collection class. In this
section we shall see that Collection<Animal> is a good choice of superclass of AnimalFarm.
The class AnimalFarm depends on the class Animal. You are invited to take a look at class Animal via the
accompanying slide . We do not include class Animal here because it does not add new insight to our interests in
collection classes. The four operations of class AnimalFarm are shown below.
using System;
using System.Collections.ObjectModel;
It is important to notice that the four highlighted operations in Program 45.3 are redefinitions of virtual, protected
methods in Collection<Animal>. Each of the methods activate the similar method in the superclass (this is method
combination). In addition, they reveal on standard output that the protected method has been called. A more realistic
example of class AnimalFarm will be presented in Program 45.6.
The four operations are not part of the client interface of class AnimalFarm. They are protected operations. The client
interface of AnimalFarm is identical to the public operations inherited from Collection<Animal>. It means that we
use the operations Add, Insert, Remove etc. on instances of class AnimalFarm.
Exercise 3:
We should now understand the role of the four protected operations InsertItem, RemoveItem, SetItem, and ClearItems
relative to the operations in the public client interface. Whenever an element is inserted into a collection, the
protected method InsertItem is called. Both Add and Insert are programmed by use of InsertItem. Similarly, both
Remove and RemoveAt are programmed by use of RemoveItem. And so on. We see that the major functionality
behind the operations in Collection<T> is controlled by the four protected methods InsertItem, RemoveItem, SetItem,
and ClearItems
using System;
using System.Collections.ObjectModel;
class App{
// Remove tiger
af.Remove(new Animal("tiger"));
ReportList("Removing tiger with Remove(...)", af);
In the example we program the following semantics of the insertion and removal operations of class AnimalFarm:
In addition, we add a GetGroup operation to AnimalFarm, which returns a collection (a sub animal farm) of all
animals that belongs to a given group (such as all birds).
using System;
using System.Collections.ObjectModel;
// Prevent removal
protected override void RemoveItem(int i){
Console.WriteLine("[Removal denied]");
}
// Prevent clearing
protected override void ClearItems(){
Console.WriteLine("[Clearing denied]");
}
}
Exercise 5: Sample use of class List<T>
In this and the following sections we will show how to use some of the operations in List<T>. We start with a basic
example in which we work on a list of characters: List<char>. We insert a number of char values into a list, and we
remove some values as well. Notice in particular how the range operations InsertRange (line 28) and RemoveRange
(line 40) operate on the list.
using System;
using System.Collections.Generic;
public static void Main(){ // List initialization and adding elements to the end of the
list:
In line 29 we show how to use the variant FindAll, which returns a Point list instead of just a single Point, as returned
by Find. In line 36 we show how IndexOf can be used to find the index of a given Point in a Point list. It is worth
asking how the Point parameter of IndexOf is compared with the points in Point list. The documentation states that
the points are compared by use of the default equality comparer of the type T, which in our case is struct Point. We
have discussed the default equality comparer in the slipstream of our coverage of the generic interfaces
IEquatable<T> and IEqualityComparer<T>.
We use the static method ReportList to show a Point list on standard output. We call ReportList several times.
using System;
using System.Collections.Generic;
class C{
System.Threading.Thread.CurrentThread.CurrentCulture =
new System.Globalization.CultureInfo("en-US");
}
public static bool FindX5(Point p){
return p.Getx() == 5;
}
Sorting the elements in a collection of elements of type T depends on a less than or equal operation on T. If the type
T is taken directly from the C# libraries, it may very well be the case that we can just use the default less than or
equal operation of the type T. If T is one of our own types, we will have to supply an implementation of the
comparison operation ourselves. This can be done by passing a delegate object to the Sort method.
Below, we illustrate most of the four overloaded Sort operations in List<T>. The actual type parameter in the
example, passed for T, is int.
using System;
using System.Collections.Generic;
class C{
Throughout the Program we do several sortings of listOriginal, as declared in line 8. In line 14 we rely the default
comparer of type int. The default comparer is explained in the following way in the .NET framework documentation
of List.Sort:
This method uses the default comparer Comparer.Default for type T to determine the order of list elements. The
Comparer.Default property checks whether type T implements the IComparable generic interface and uses that
implementation, if available. If not, Comparer.Default checks whether type T implements the IComparable interface.
If type T does not implement either interface, Comparer.Default throws an InvalidOperationException.
The sorting done in line 21 is equivalent to line 14. In line 21 we show how to pass the default comparer of type int
explicitly to the Sort method.
Let us now assume the type int does not have a default comparer. In other words, we will have to implement the
comparer ourselves. The call of Sort in line 28 passes a new IntComparer instance to Sort. The class IntComparer is
programmed in line 53-61, at the bottom. Notice that IntComparer is a subclass of Comparer<int>, which is an
abstract class in the namespace System.Collections.Genericwith an abstract method named Compare. The generic
class Comparer<T> is in many ways similar to the class EqualityComparer<T>. Most important, both have a static
Default property, which returns a comparer object.
As a final resort that always works we can pass a comparer function to Sort. In C#, such a function is programmed as
a delegate. (Delegates are discussed in Chapter 22). Line 35-40 shows how this can be done. Notice that the delegate
we use is programmed on the fly. This style of programming is a reminiscence of functional programming.
I find it much more natural to pass an ordering method instead of an object of a class with an ordering method. (The
latter is a left over from older object-oriented programming languages in which the only way to pass a function F as
parameter is via an object of a class in which F is an instance method). In general, I also prefer to be explicit about
the ordering instead of relying on some default ordering which may turn out to surprise you.
Let us summarize the lessons that we have learned from the example:
Some types have a default comparer which is used by List.Sort()
The default comparer of T can extracted by Comparer<T>.Default
An anonymous delegate comparer is attractive if the default comparer of the type does not exist, of if it is
inappropriate.
The BinarySearch operations in List<T> require, as a precondition, that the list is ordered before the search is
performed. If necessary, the Sort operation (see Section 45.12) can be used to establish the ordering.
You may ask why we should search for an element which we - in the starting point - is able to pass as input to the
BinarySearch method. There is a couple of good answers. First, we may be interested to know if the element is
present or not in the list. Second, it may also be possible to search for an incomplete object (by only comparing some
selected fields in the Comparer method). Using this approach we are actually interested in finding the complete
object, with all the data fields, in the collection.
If the BinarySearch operation finds an element in the list, the index of the element is returned. This is a non- negative
integer. If the element is not found, a negative integer, say i, is returned. Below we will see that that -i (or more
precisely the bitwise complement ~i) in that case is the position of the element, if it had been present in the list.
using System;
using System.Collections.Generic;
class BinarySearchDemo{
System.Threading.Thread.CurrentThread.CurrentCulture =
new System.Globalization.CultureInfo("en-US");
int res;
Point searchPoint;
// Run-time error.
// Failed to compare two elements in the array.
// searchPoint = new Point(5,4);
// res = pointLst.BinarySearch(searchPoint);
// Console.WriteLine("BinarySearch for {0}: {1}", searchPoint, res);
class BinarySearchDemo{
System.Threading.Thread.CurrentThread.CurrentCulture =
new System.Globalization.CultureInfo("en-US");
int res;
Point searchPoint;
Contrary to Sort, it is not possible to pass a delegate to BinarySearch. This seems to be a flaw in the design of the
List<T> library.
using System;
using System.Collections.Generic;
class LinkedListDemo{
// Using Add.
// Compile-time error: 'LinkedList<int>' does not contain a
// definition for 'Add'
// lst.Add(17);
// ReportList("lst.Add(17);" lst);
// Add is implemented as an explicit interface implementation
((ICollection<int>)lst).Add(17);
ReportList("((ICollection<int>)lst).Add(17);", lst);
// Using Remove.
lst.Remove(17);
ReportList("lst.Remove(17);", lst);
// Using Clear
lst.Clear();
ReportList("lst.Clear();", lst);
Initial LinkedList
5 3 2 7 -4 0
((ICollection<int>)lst).Add(17);
5 3 2 7 -4 0 17
lst.AddFirst(-88); lst.AddFirst(88);
-88 5 3 2 7 -4 0 17 88
lst.Remove(17);
-88 5 3 2 7 -4 0 88
Program 45.19 Basic operations on a LinkedList of integers.
Listing 45.20 Output of the program with basic operations on a LinkedList.
The LinkedList example in Program 45.19 did not show how to use LinkedListNodes
together with LinkedList<T>. To make up for that we will in Program 45.21 concentrate on
the use of LinkedList<T> and LinkedListNode<T> together.
using System;
using System.Collections.Generic;
class LinkedListNodeDemo{
// Run-time error.
// The LinkedListNode is already in the list.
// Error message: The LinkedList node belongs a LinkedList.
/* lst.AddLast(node1); */
}
Program 45.21 Basic operations on a LinkedList of integers - using LinkedListNodes.
In line 8-9 we make the same initial integer list as in Program 45.19. In line 13-14 we see
how to access to the first/last LinkedListNode objects of the list.
In line 19 we attempt to add node1, which is the first LinkedListNode in lst, as the last node
of the list. This fails because it could bring the linked list into an inconsistent state. (Recall in
this context that a LinkedListNode knows the list to which it belongs). Instead, as shown in
line 22, we should first remove node1 and then add node1 with AddLast.
Exercise 11: Using Collections through Interfaces
Let us illustrate how this can be done in Program 45.23. The thing to notice is that the only
place we refer to a list class (here Collection<Animal>() ) is in line 9: new
Collection<Animal>. All other places, as emphasized with purple, we use the interface
ICollection<Animal>. If we, tomorrow, wish to change the representation of the animal
collection, the only place to modify is line 9.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
class CollectionInterfaceDemo{
public static void Main(){
ICollection<Animal> lst = new Collection<Animal>();
foreach(Animal a in list)
if (a.Sex == Sex.Male) numberOfMales++;
else if (a.Sex == Sex.Female) numberOfFemales++;
foreach(Animal a in list)
if (a.Group == AnimalGroup.Mammal) numberOfMammals++;
else if (a.Group == AnimalGroup.Bird) numberOfBirds++;
else if (a.Group == AnimalGroup.Fish) numberOfFish++;
}
Program 45.23 A program based on ICollection<Animal> - with a Collection<Animal>.
On the accompanying slide we show versions of Program 45.23, which are tightly bound to
the class Collection<Animal>, and we show a version in which we have replaced
Collection<Animal> with List<Animal>.
Exercise 12: Sample use of class Dictionary<K,V>
In this section we will illustrate the use of dictionaries with a simple example. We go for a
dictionary that maps objects of type Person to objects of type BankAccount. Given a Person
object (the key) we wish to have efficient access to the person's BankAccount (the value).
The class Person is similar to Program 20.3. The class BankAccount is similar to Program
25.1. The exact versions of Person and BankAccount, as used in the dictionary example, can
be accessed via the accompanying slide page, or via the program index of this lecture.
using System;
using System.Collections.Generic;
class DictionaryDemo{
In line 52-53 we illustrate TryGetValue. First, in line 52, we attempt to access Maria's
account. The out parameter baRes1 is assigned to Maria's account and true is returned from
the method. In line 53 we attempt to access the account of a brand new Person object, which
has no bank account in the dictionary. null is returned through ba2Res, and false is returned
from the method.
Finally, in line 58-64 we remove entries from the dictionary by use of the Remove method.
First Kurt's entry is removed after which Francoi's entry is removed.
Can you tell the difference between the output of the program on this slide and the output of
your revised program?
You can access the BankAccount and Person classes in the web version of the material.