Programming in C#
Collections
CSE 494R
(proposed course for 459 Programming in C#)
Prof. Roger Crawfis
Collection Namespaces
 The .NET framework provides several
components that aid in using collections, either
those directly implemented in .NET or your
own custom collections.
 The collection namespaces are:
Namespace Contains
System.Collections Nongeneric collection classes and interfaces.
System.Collections.Specialized Strongly typed nongeneric collection classes.
System.Collections.Generic Generic collection classes and interfaces.
System.Collections.ObjectModel Proxies and base classes for custom collections.
Flavors of Collections
Scattered across these namespaces are
components that:
 Provide standard interfaces for working with a
broad range of collection implementations.
 Provide robust and complete concrete
implementations for common data structures such
as linked-lists, dynamic arrays and partial maps.
 Provide wrappers around a few of these concrete
classes for user customization.
 Provide a wrapper or proxy that limits the
accessibility to read-only.
Generic vs. non-Generic
The Generic collection interfaces and
classes provide strong type safety and
should almost always be preferred over
the non-generic collections introduced in
C# 1.0
Many of the generic interfaces are
derived from the non-generic interfaces,
so we will cover both.
What is a Collection?
A collection can provide various degrees of
functionality.
At a minimum it needs to provide some
mechanism to list or enumerate its
members.
Other useful, but non-essential functionality
might include adding and removing items,
clearing the collection, indexing into the
collection, …
Enumeration
Basic construct of any enumerator:
public interface IEnumerator
{
bool MoveNext();
object Current { get; }
void Reset();
}
Enumeration
Basic construct of any enumerator:
public interface IEnumerator
{
bool MoveNext();
object Current { get; }
void Reset();
}
This is one of the beauties
of type unification.
Question and an action
Called before Current
All interfaces
in .NET have an
initial letter of I
Bad Enumerator Design
Using an enumerator is quite easy:
class MyCollection : IEnumerator { … }
MyCollection animals;
…
animals.Reset();
while (animals.MoveNext())
{
Animal pet = (Animal) animals.Current;
…
}
As we will see shortly, the foreach
statement provides a much cleaner
implementation.
Bad Enumerator Design
Using an enumerator is quite easy:
class MyCollection : IEnumerator { … }
MyCollection animals;
…
animals.Reset();
while (animals.MoveNext())
{
Animal pet = (Animal) animals.Current;
…
}
Non-generic requires a
type conversion.
As we will see shortly, the foreach
statement provides a much cleaner
implementation.
Iterator Design Pattern
 Intent
 Access elements of a container without exposing
its representation
 Applicability
 Require multiple traversal algorithms over a
container
 Require a uniform traversal interface over
different containers
 When container classes & traversal algorithm
must vary independently
Iterator Design Pattern
 Some collections may have more than one
way to iterate over them (e.g., graphs).
 Solution: Separate the iteration from the class.
IEnumerable
Collections in .NET use the Iterator
Design Pattern, hence they do not
implement IEnumerator.
Instead they implement IEnumerable.
Creates a new instance of an IEnumerator
and returns it.
public interface IEnumerable {
IEnumerator GetEnumerator();
}
Generic Interfaces
public interface IEnumerator<T> : IEnumerator, IDisposable
{
T Current { get; }
}
public interface IEnumerable<T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
Enumerating Collections
Let’s look at how we use these
interfaces first, then we will look at how
we might implement them in our own
collections.
Enumerating Collections
class MyCollection : IEnumerable { … }
MyCollection animals;
…
IEnumerator animalIterator = animals.GetEnumerator();
animalIterator.Reset(); // This is usually not needed.
while (animalIterator.MoveNext())
{
Animal pet = (Animal) animalIterator.Current;
IEnumerator iterator = animals.GetEnumerator();
while (…) { … }
…
}
Enumerating Collections
class MyCollection : IEnumerable { … }
MyCollection animals;
…
IEnumerator animalIterator = animals.GetEnumerator();
animalIterator.Reset(); // This is usually not needed.
while (animalIterator.MoveNext())
{
Animal pet = (Animal) animalIterator.Current;
IEnumerator iterator = animals.GetEnumerator();
while (…) { … }
…
}
Nested
iterators!
Old outdated approach!!!
Enumerating Collections
class MyCollection : IEnumerable <Animal> { … }
MyCollection animals;
…
IEnumerator<Animal> animalIterator
= animals.GetEnumerator();
while (animalIterator.MoveNext())
{
Animal pet = animalIterator.Current;
IEnumerator<Animal> iterator = animals.GetEnumerator();
while (…) { … }
…
}
Enumerating Collections
class MyCollection : IEnumerable <Animal> { … }
MyCollection animals;
…
IEnumerator<Animal> animalIterator
= animals.GetEnumerator();
while (animalIterator.MoveNext())
{
Animal pet = animalIterator.Current;
IEnumerator<Animal> iterator = animals.GetEnumerator();
while (…) { … }
…
}
No type casting!
Still an old outdated approach!!!
Enumerating Collections
Compiler syntax shortcut – the foreach
class MyCollection : IEnumerable <Animal> { … }
MyCollection animals;
…
foreach(Animal pet in animals)
{
foreach (…) { … }
…
}
The foreach can be used with any
IEnumerable collection!!!
Enumerable Collections
What collections are enumerable?
That is, support the IEnumerable or
IEnumerable<T> interfaces?
System.Array – all arrays support
IEnumerable<T>
All .NET collections except dictionaries.
System.String or string.
Any user-defined class supporting the
interface.
Enumerable Collections
readonly char[] vowels = { 'a','e‘,'i','o','u' };
void AddCorpus( string corpus ) {
foreach (char letter in corpus)
foreach (char vowel in vowels)
if (vowel == letter) …
}
Implementing Iterators
Implementing the IEnumerable interface
provides:
Automatic support for the foreach loop.
Programming to interfaces
Implementing Iterators
Collections that wrap another collection:
public class Graph : IEnumerable<GraphNode>
{
private IList<GraphNode> nodes;
public IEnumerator<GraphNode> GetEnumerator() {
return nodes.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
Implementing Iterators
Collections that wrap another collection:
public class Graph : IEnumerable<GraphNode>
{
private IList<GraphNode> nodes;
public IEnumerator<GraphNode> GetEnumerator() {
return nodes.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
IList is deriverd from
the IEnumerable<T>
interface.
Implementing Iterators
Using the yield return operation.
public class Graph : IEnumerable<string>
{
private IList<GraphNode> nodes;
public IEnumerator<string> GetEnumerator() {
foreach (GraphNode node in nodes) {
yield return node.Label;
}
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
Implementing Iterators
 What is happening here?
 Are we returning strings or IEnumerators?
 Did we just lose the decoupling of the iterator
from the class?
 Can we still have multiple concurrent
iterators?
 To better understand what the compiler is
doing, let’s look at an explicit
implementation.
Implementing Iterators
class GraphNodeEnumerator : IEnumerator<string>
{
#region IEnumerator<string> Members
public string Current
{
get { return graph.nodes[currentIndex].Label; }
}
#endregion
Implementing Iterators
#region IEnumerator Members
object IEnumerator.Current
{
get { return Current; }
}
public bool MoveNext()
{
return ++currentIndex < graph.nodes.Count;
}
public void Reset()
{
currentIndex = -1;
}
#endregion
Implementing Iterators
private int currentIndex = -1;
private Graph graph;
internal GraphNodeEnumerator(Graph graph)
{
this.graph = graph;
}
}
Implementing Iterators
In order for this to work, the iterator
needs access to the Graph’s private
data. This requires a nested class.
Note that the class can be private, we
will only expose it as an instance of type
IEnumerator.
Implementing Iterators
OK, so back to the yield return.
Note, that most of the explicit
implementation is rather boiler-plate.
The compiler will take the statements in
the GetEnumerator and embed them into
a hidden nested class that it creates.
Implementing Iterators
Can have multiple yield returns.
internal class PrimaryColors : IEnumerable<Color>
{
#region IEnumerable<Color> Members
public IEnumerator<Color> GetEnumerator()
{
yield return System.Drawing.Color.Red;
yield return System.Drawing.Color.Green;
yield return System.Drawing.Color.Blue;
}
#endregion
#region IEnumerable Members
…
}
Implementing Iterators
The yield break clause allows you to
exit the iterator early or unexpectedly.
public static IEnumerable<int> Range(int min, int max)
{
while (true)
{
if (min >= max)
{
yield break;
}
yield return min++;
}
}
Implementing Iterators
Can have multiple iterators returned from
properties, Reverse, …
For the Graph, perhaps I do not want it to
be Enumerable, but to have 2 properties,
Nodes, and Edges, each of which a
IEnumerable.
May also want Depth-first, post-order
traversal of the nodes starting from a
particular node.
Implementing Iterators
public class TowersOfHanoi
{
public enum Peg { A, B, C }
public struct Move
{
public Move(Peg from, Peg to) { From = from; To = to; }
public readonly Peg From;
public readonly Peg To;
}
public static IEnumerable<Move> GenerateMoves(int n, Peg from, Peg to, Peg scratch)
{
if (n == 1) yield return new Move(from, to);
else
{
foreach (Move m in GenerateMoves(n - 1, from, scratch, to)) yield return m;
yield return new Move(from, to);
foreach (Move m in GenerateMoves(n - 1, scratch, to, from)) yield return m;
}
}
}
recursive calls
Implementing Iterators
public class TowersOfHanoi
{
public enum Peg { A, B, C }
public struct Move
{
public Move(Peg from, Peg to) { From = from; To = to; }
public readonly Peg From;
public readonly Peg To;
}
public static IEnumerable<Move> GenerateMoves(int n, Peg from, Peg to, Peg scratch)
{
if (n == 1) yield return new Move(from, to);
else
{
foreach (Move m in GenerateMoves(n - 1, from, scratch, to)) yield return m;
yield return new Move(from, to);
foreach (Move m in GenerateMoves(n - 1, scratch, to, from)) yield return m;
}
}
}
recursive calls
public static void Main()
{
foreach(Move m in ToweresOfHanoi.GenerateMoves(4, Peg.A, Peg.B, Peg.C))
{
Console.WriteLine("From {0} to {1}", m.From, m.To);
}
}
From A to C
From A to B
From C to B
From A to C
From B to A
From B to C
From A to C
From A to B
From C to B
From C to A
From B to A
From C to B
From A to C
From A to B
From C to B
As with almost all recursion, cute,
but not very efficient!
Implementing Iterators
 For collections having a key/value pair, such
as the Dictionary classes we will discuss later
a separate interface is built ontop of the
IEnumerator interfaces:
 IDictionaryEnumerator : IEnumerator
 IDictionaryEnumerator<KeyValuePair<TKey,TValue>> :
IEnumerator<KeyValuePair<TKey,TValue>>
 These interfaces replace the Current property
with an Entry property, a Key property and a
Value property.
Enumerable Collections
public class VowelHistogram
{
static readonly string vowels = "aeiou";
public IDictionary<char, int> VowelCounts { get { return vowelCounts; } }
void AddCorpus( string corpus )
{
foreach (char letter in corpus)
foreach (char vowel in vowels)
if (vowel == letter)
vowelCounts[vowel] += 1;
}
private IDictionary<char, int> vowelCounts = new Dictionary<char, int>(5);
}
Programming in C#
Collections
CSE 494R
(proposed course for 459 Programming in C#)
Prof. Roger Crawfis

Announcemet_CCCSharp_07_Enumeration.pptx

  • 1.
    Programming in C# Collections CSE494R (proposed course for 459 Programming in C#) Prof. Roger Crawfis
  • 2.
    Collection Namespaces  The.NET framework provides several components that aid in using collections, either those directly implemented in .NET or your own custom collections.  The collection namespaces are: Namespace Contains System.Collections Nongeneric collection classes and interfaces. System.Collections.Specialized Strongly typed nongeneric collection classes. System.Collections.Generic Generic collection classes and interfaces. System.Collections.ObjectModel Proxies and base classes for custom collections.
  • 3.
    Flavors of Collections Scatteredacross these namespaces are components that:  Provide standard interfaces for working with a broad range of collection implementations.  Provide robust and complete concrete implementations for common data structures such as linked-lists, dynamic arrays and partial maps.  Provide wrappers around a few of these concrete classes for user customization.  Provide a wrapper or proxy that limits the accessibility to read-only.
  • 4.
    Generic vs. non-Generic TheGeneric collection interfaces and classes provide strong type safety and should almost always be preferred over the non-generic collections introduced in C# 1.0 Many of the generic interfaces are derived from the non-generic interfaces, so we will cover both.
  • 5.
    What is aCollection? A collection can provide various degrees of functionality. At a minimum it needs to provide some mechanism to list or enumerate its members. Other useful, but non-essential functionality might include adding and removing items, clearing the collection, indexing into the collection, …
  • 6.
    Enumeration Basic construct ofany enumerator: public interface IEnumerator { bool MoveNext(); object Current { get; } void Reset(); }
  • 7.
    Enumeration Basic construct ofany enumerator: public interface IEnumerator { bool MoveNext(); object Current { get; } void Reset(); } This is one of the beauties of type unification. Question and an action Called before Current All interfaces in .NET have an initial letter of I
  • 8.
    Bad Enumerator Design Usingan enumerator is quite easy: class MyCollection : IEnumerator { … } MyCollection animals; … animals.Reset(); while (animals.MoveNext()) { Animal pet = (Animal) animals.Current; … } As we will see shortly, the foreach statement provides a much cleaner implementation.
  • 9.
    Bad Enumerator Design Usingan enumerator is quite easy: class MyCollection : IEnumerator { … } MyCollection animals; … animals.Reset(); while (animals.MoveNext()) { Animal pet = (Animal) animals.Current; … } Non-generic requires a type conversion. As we will see shortly, the foreach statement provides a much cleaner implementation.
  • 10.
    Iterator Design Pattern Intent  Access elements of a container without exposing its representation  Applicability  Require multiple traversal algorithms over a container  Require a uniform traversal interface over different containers  When container classes & traversal algorithm must vary independently
  • 11.
    Iterator Design Pattern Some collections may have more than one way to iterate over them (e.g., graphs).  Solution: Separate the iteration from the class.
  • 12.
    IEnumerable Collections in .NETuse the Iterator Design Pattern, hence they do not implement IEnumerator. Instead they implement IEnumerable. Creates a new instance of an IEnumerator and returns it. public interface IEnumerable { IEnumerator GetEnumerator(); }
  • 13.
    Generic Interfaces public interfaceIEnumerator<T> : IEnumerator, IDisposable { T Current { get; } } public interface IEnumerable<T> : IEnumerable { IEnumerator<T> GetEnumerator(); }
  • 14.
    Enumerating Collections Let’s lookat how we use these interfaces first, then we will look at how we might implement them in our own collections.
  • 15.
    Enumerating Collections class MyCollection: IEnumerable { … } MyCollection animals; … IEnumerator animalIterator = animals.GetEnumerator(); animalIterator.Reset(); // This is usually not needed. while (animalIterator.MoveNext()) { Animal pet = (Animal) animalIterator.Current; IEnumerator iterator = animals.GetEnumerator(); while (…) { … } … }
  • 16.
    Enumerating Collections class MyCollection: IEnumerable { … } MyCollection animals; … IEnumerator animalIterator = animals.GetEnumerator(); animalIterator.Reset(); // This is usually not needed. while (animalIterator.MoveNext()) { Animal pet = (Animal) animalIterator.Current; IEnumerator iterator = animals.GetEnumerator(); while (…) { … } … } Nested iterators! Old outdated approach!!!
  • 17.
    Enumerating Collections class MyCollection: IEnumerable <Animal> { … } MyCollection animals; … IEnumerator<Animal> animalIterator = animals.GetEnumerator(); while (animalIterator.MoveNext()) { Animal pet = animalIterator.Current; IEnumerator<Animal> iterator = animals.GetEnumerator(); while (…) { … } … }
  • 18.
    Enumerating Collections class MyCollection: IEnumerable <Animal> { … } MyCollection animals; … IEnumerator<Animal> animalIterator = animals.GetEnumerator(); while (animalIterator.MoveNext()) { Animal pet = animalIterator.Current; IEnumerator<Animal> iterator = animals.GetEnumerator(); while (…) { … } … } No type casting! Still an old outdated approach!!!
  • 19.
    Enumerating Collections Compiler syntaxshortcut – the foreach class MyCollection : IEnumerable <Animal> { … } MyCollection animals; … foreach(Animal pet in animals) { foreach (…) { … } … } The foreach can be used with any IEnumerable collection!!!
  • 20.
    Enumerable Collections What collectionsare enumerable? That is, support the IEnumerable or IEnumerable<T> interfaces? System.Array – all arrays support IEnumerable<T> All .NET collections except dictionaries. System.String or string. Any user-defined class supporting the interface.
  • 21.
    Enumerable Collections readonly char[]vowels = { 'a','e‘,'i','o','u' }; void AddCorpus( string corpus ) { foreach (char letter in corpus) foreach (char vowel in vowels) if (vowel == letter) … }
  • 22.
    Implementing Iterators Implementing theIEnumerable interface provides: Automatic support for the foreach loop. Programming to interfaces
  • 23.
    Implementing Iterators Collections thatwrap another collection: public class Graph : IEnumerable<GraphNode> { private IList<GraphNode> nodes; public IEnumerator<GraphNode> GetEnumerator() { return nodes.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
  • 24.
    Implementing Iterators Collections thatwrap another collection: public class Graph : IEnumerable<GraphNode> { private IList<GraphNode> nodes; public IEnumerator<GraphNode> GetEnumerator() { return nodes.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } IList is deriverd from the IEnumerable<T> interface.
  • 25.
    Implementing Iterators Using theyield return operation. public class Graph : IEnumerable<string> { private IList<GraphNode> nodes; public IEnumerator<string> GetEnumerator() { foreach (GraphNode node in nodes) { yield return node.Label; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
  • 26.
    Implementing Iterators  Whatis happening here?  Are we returning strings or IEnumerators?  Did we just lose the decoupling of the iterator from the class?  Can we still have multiple concurrent iterators?  To better understand what the compiler is doing, let’s look at an explicit implementation.
  • 27.
    Implementing Iterators class GraphNodeEnumerator: IEnumerator<string> { #region IEnumerator<string> Members public string Current { get { return graph.nodes[currentIndex].Label; } } #endregion
  • 28.
    Implementing Iterators #region IEnumeratorMembers object IEnumerator.Current { get { return Current; } } public bool MoveNext() { return ++currentIndex < graph.nodes.Count; } public void Reset() { currentIndex = -1; } #endregion
  • 29.
    Implementing Iterators private intcurrentIndex = -1; private Graph graph; internal GraphNodeEnumerator(Graph graph) { this.graph = graph; } }
  • 30.
    Implementing Iterators In orderfor this to work, the iterator needs access to the Graph’s private data. This requires a nested class. Note that the class can be private, we will only expose it as an instance of type IEnumerator.
  • 31.
    Implementing Iterators OK, soback to the yield return. Note, that most of the explicit implementation is rather boiler-plate. The compiler will take the statements in the GetEnumerator and embed them into a hidden nested class that it creates.
  • 32.
    Implementing Iterators Can havemultiple yield returns. internal class PrimaryColors : IEnumerable<Color> { #region IEnumerable<Color> Members public IEnumerator<Color> GetEnumerator() { yield return System.Drawing.Color.Red; yield return System.Drawing.Color.Green; yield return System.Drawing.Color.Blue; } #endregion #region IEnumerable Members … }
  • 33.
    Implementing Iterators The yieldbreak clause allows you to exit the iterator early or unexpectedly. public static IEnumerable<int> Range(int min, int max) { while (true) { if (min >= max) { yield break; } yield return min++; } }
  • 34.
    Implementing Iterators Can havemultiple iterators returned from properties, Reverse, … For the Graph, perhaps I do not want it to be Enumerable, but to have 2 properties, Nodes, and Edges, each of which a IEnumerable. May also want Depth-first, post-order traversal of the nodes starting from a particular node.
  • 35.
    Implementing Iterators public classTowersOfHanoi { public enum Peg { A, B, C } public struct Move { public Move(Peg from, Peg to) { From = from; To = to; } public readonly Peg From; public readonly Peg To; } public static IEnumerable<Move> GenerateMoves(int n, Peg from, Peg to, Peg scratch) { if (n == 1) yield return new Move(from, to); else { foreach (Move m in GenerateMoves(n - 1, from, scratch, to)) yield return m; yield return new Move(from, to); foreach (Move m in GenerateMoves(n - 1, scratch, to, from)) yield return m; } } } recursive calls
  • 36.
    Implementing Iterators public classTowersOfHanoi { public enum Peg { A, B, C } public struct Move { public Move(Peg from, Peg to) { From = from; To = to; } public readonly Peg From; public readonly Peg To; } public static IEnumerable<Move> GenerateMoves(int n, Peg from, Peg to, Peg scratch) { if (n == 1) yield return new Move(from, to); else { foreach (Move m in GenerateMoves(n - 1, from, scratch, to)) yield return m; yield return new Move(from, to); foreach (Move m in GenerateMoves(n - 1, scratch, to, from)) yield return m; } } } recursive calls public static void Main() { foreach(Move m in ToweresOfHanoi.GenerateMoves(4, Peg.A, Peg.B, Peg.C)) { Console.WriteLine("From {0} to {1}", m.From, m.To); } } From A to C From A to B From C to B From A to C From B to A From B to C From A to C From A to B From C to B From C to A From B to A From C to B From A to C From A to B From C to B As with almost all recursion, cute, but not very efficient!
  • 37.
    Implementing Iterators  Forcollections having a key/value pair, such as the Dictionary classes we will discuss later a separate interface is built ontop of the IEnumerator interfaces:  IDictionaryEnumerator : IEnumerator  IDictionaryEnumerator<KeyValuePair<TKey,TValue>> : IEnumerator<KeyValuePair<TKey,TValue>>  These interfaces replace the Current property with an Entry property, a Key property and a Value property.
  • 38.
    Enumerable Collections public classVowelHistogram { static readonly string vowels = "aeiou"; public IDictionary<char, int> VowelCounts { get { return vowelCounts; } } void AddCorpus( string corpus ) { foreach (char letter in corpus) foreach (char vowel in vowels) if (vowel == letter) vowelCounts[vowel] += 1; } private IDictionary<char, int> vowelCounts = new Dictionary<char, int>(5); }
  • 39.
    Programming in C# Collections CSE494R (proposed course for 459 Programming in C#) Prof. Roger Crawfis