◎Two Main Issue for non-Generic Types
▶Performance
▶Safety
◎System.Collections
⊙Interfaces of System.Collections
⊙Old version and New version
▶Performance : Generic Classes is Object Type, but that are deal with Object and Primitive type both
▶Type Safety
⊙Benefit of Generic containers
▶Generics provide better performance because they do not result in boxing or unboxing penalties.
⊙Sort an array by Sort<T>()▶Performance
▶Safety
◎System.Collections
Class | Key Points | Interface |
ArrayList | Dynamic Array size Sequential Order |
ICloneable, IEnumerable, ICollection, IList |
Queue | FIFO | ICloneable, IEnumerable, ICollection |
Stack | LIFO PUSH/POP |
ICloneable, IEnumerable, ICollection |
Hashtable | Key/Value has code |
ICloneable, IEnumerable, ICollection, IDictionary |
SortedList | Key/Value Sorted Accessable by key and Index |
ICloneable, IEnumerable, ICollection, IDictionary |
⊙Interfaces of System.Collections
Interfaces | Key Points |
ICollection | Defined general characteristics -size, -enumeration -thread safety |
ICloneable | Deep Copy |
IDictionary | Allows non-Generic Collection Object Key/Value |
IEnumerable | return IEnumerator |
IEnumerator | Eables foreach loop to iterate of collection items |
IList | Provide methods -add -remove -index |
⊙Old version and New version
System.Collections | |
System.Collections.Generic | |
System.Collections.Specialized |
▶Performance : Generic Classes is Object Type, but that are deal with Object and Primitive type both
Primitive
UnBoxing
Stack
|
<--- Type Conversion --->
Performance
|
Object Boxing Heap |
▶Type Safety
User Type Example |
public class IntCollection : IEnumerable { private ArrayList arInts = new ArrayList(); // Unbox for caller. public int GetInt(int pos) { return (int)arInts[pos]; } // Boxing operation! public void AddInt(int i) { arInts.Add(i); } public void ClearInts() { arInts.Clear(); } public int Count { get { return arInts.Count; } } IEnumerator IEnumerable.GetEnumerator() { return arInts.GetEnumerator(); } } |
System.Collections.Generic Type |
static void UseGenericList() { Console.WriteLine("***** Fun with Generics *****\n"); // This List<> can only hold Person objects. List morePeople = new List(); morePeople.Add(new Person ("Frank", "Black", 50)); Console.WriteLine(morePeople[0]); // This List<> can only hold integers. List moreInts = new List(); moreInts.Add(10); moreInts.Add(2); int sum = moreInts[0] + moreInts[1]; // Compile-time error! Can't add Person object // to a list of ints! // moreInts.Add(new Person()); } |
⊙Benefit of Generic containers
▶Generics provide better performance because they do not result in boxing or unboxing penalties.
▶Generics are more type safe because they can only contain the type of type you specify.
▶Generics greatly reduce the need to build custom collection types because the base class library provides several prefabricated containers.
Array class has static Sort generic method |
int[] myInts = { 10, 4, 2, 33, 93 }; // Specify the placeholder to the generic // Sort<>() method. Array.Sort<int>(myInts); foreach (int i in myInts) { Console.WriteLine(i); } |
⊙Comparing Generic vs. non-Generic user defined Interfaces
Non-Generic User defined Interface |
public class Car : IComparable { ... // IComparable implementation. int IComparable.CompareTo(object obj) { Car temp = obj as Car; ----Must need check the Type if (temp != null) { if (this.CarID > temp.CarID) return 1; if (this.CarID < temp.CarID) return -1; else return 0; } else throw new ArgumentException("Parameter is not a Car!"); } } |
Generic User defined Interface |
public class Car : IComparable<Car> { ... // IComparable<T> implementation. int IComparable<Car>.CompareTo(Car obj) -------surely this is only for Car type { if (this.CarID > obj.CarID) return 1; if (this.CarID < obj.CarID) return -1; else return 0; } } |
⊙Generic interface namespaces
ICollection<T> | Defines general characteristics (e.g., size, enumeration, and thread safety) for all generic collection types. |
IComparer<T> | Defines a way to compare to objects. |
IDictionary<TKey, TValue> | Allows a generic collection object to represent its contents using key/value pairs. |
IEnumerable<T> | Returns the IEnumerator<T> interface for a given object. |
IEnumerator<T> | Enables foreach-style iteration over a generic collection. |
IList<T> | Provides behavior to add, remove, and index items in a sequential list of objects. |
ISet<T> *from .Net4 | Provides the base interface for the abstraction of sets. |
⊙Generic class namespaces
Generic Class | Supported Key | Interfaces Meaning in Life |
Dictionary<TKey, TValue> | ICollection<T>,IDictionary<TKey, TValue>, IEnumerable<T> | This represents a generic collection of keys and values. |
List<T> | ICollection<T>,IEnumerable<T>, IList<T> | This is a dynamically resizable sequential list of items. |
LinkedList<T> | ICollection<T>,IEnumerable<T> | This represents a doubly linked list. |
Queue<T> | ICollection (not a typo! This is the non-generic collection interface), IEnumerable<T> | This is a generic implementation of a first-in, first-out (FIFO) list. |
SortedDictionary<TKey, TValue> | ICollection<T>, IDictionary<TKey, TValue>, IEnumerable<T> | This is a generic implementation of a sorted set of key/value pairs. |
SortedSet<T> | ICollection<T>, IEnumerable<T>, ISet<T> | This represents a collection of objects that is maintained in sorted order with no duplication. |
Stack<T> | ICollection (not a typo! This is the non-generic collection interface), IEnumerable<T> | This is a generic implementation of a last-in, first-out (LIFO) list. |
⊙Collection Initialization Syntax
// Init a standard array. int[] myArrayOfInts = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // Init a generic List<> of ints. List<int> myGenericList = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // Init an ArrayList with numerical data. ArrayList myList = new ArrayList { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; |
⊙Example List<T>
private static void UseGenericList() { // Make a List of Person objects, filled with // collection / object init syntax. List⊙Example Stack<T>people = new List () { new Person {FirstName= "Homer", LastName="Simpson", Age=47}, new Person {FirstName= "Marge", LastName="Simpson", Age=45}, new Person {FirstName= "Lisa", LastName="Simpson", Age=9}, new Person {FirstName= "Bart", LastName="Simpson", Age=8} }; // Print out # of items in List. Console.WriteLine("Items in list: {0}", people.Count); // Enumerate over list. foreach (Person p in people) Console.WriteLine(p); // Insert a new person. Console.WriteLine("\n->Inserting new person."); people.Insert(2, new Person { FirstName = "Maggie", LastName = "Simpson", Age = 2 }); Console.WriteLine("Items in list: {0}", people.Count); // Copy data into a new array. Person[] arrayOfPeople = people.ToArray(); for (int i = 0; i < arrayOfPeople.Length; i++) { Console.WriteLine("First Names: {0}", arrayOfPeople[i].FirstName); } } ***** Fun with Generic Collections ***** Items in list: 4 Name: Homer Simpson, Age: 47 Name: Marge Simpson, Age: 45 Name: Lisa Simpson, Age: 9 Name: Bart Simpson, Age: 8 ->Inserting new person. Items in list: 5 First Names: Homer First Names: Marge First Names: Maggie First Names: Lisa First Names: Bart
static void UseGenericStack() { Stack⊙Example Queue<T>stackOfPeople = new Stack (); stackOfPeople.Push(new Person { FirstName = "Homer", LastName = "Simpson", Age = 47 }); stackOfPeople.Push(new Person { FirstName = "Marge", LastName = "Simpson", Age = 45 }); stackOfPeople.Push(new Person { FirstName = "Lisa", LastName = "Simpson", Age = 9 }); // Now look at the top item, pop it, and look again. Console.WriteLine("First person is: {0}", stackOfPeople.Peek()); Console.WriteLine("Popped off {0}", stackOfPeople.Pop()); Console.WriteLine("\nFirst person is: {0}", stackOfPeople.Peek()); Console.WriteLine("Popped off {0}", stackOfPeople.Pop()); Console.WriteLine("\nFirst person item is: {0}", stackOfPeople.Peek()); Console.WriteLine("Popped off {0}", stackOfPeople.Pop()); try { Console.WriteLine("\nnFirst person is: {0}", stackOfPeople.Peek()); Console.WriteLine("Popped off {0}", stackOfPeople.Pop()); } catch (InvalidOperationException ex) { Console.WriteLine("\nError! {0}", ex.Message); } } ***** Fun with Generic Collections ***** First person is: Name: Lisa Simpson, Age: 9 Popped off Name: Lisa Simpson, Age: 9 First person is: Name: Marge Simpson, Age: 45 Popped off Name: Marge Simpson, Age: 45 First person item is: Name: Homer Simpson, Age: 47 Popped off Name: Homer Simpson, Age: 47 Error! Stack empty.
static void GetCoffee(Person p) { Console.WriteLine("{0} got coffee!", p.FirstName); } static void UseGenericQueue() { // Make a Q with three people. Queue⊙Example SortedSet<T>peopleQ = new Queue (); peopleQ.Enqueue(new Person {FirstName= "Homer", LastName="Simpson", Age=47}); peopleQ.Enqueue(new Person {FirstName= "Marge", LastName="Simpson", Age=45}); peopleQ.Enqueue(new Person {FirstName= "Lisa", LastName="Simpson", Age=9}); // Peek at first person in Q. Console.WriteLine("{0} is first in line!", peopleQ.Peek().FirstName); // Remove each person from Q. GetCoffee(peopleQ.Dequeue()); GetCoffee(peopleQ.Dequeue()); GetCoffee(peopleQ.Dequeue()); // Try to de-Q again? try { GetCoffee(peopleQ.Dequeue()); } catch(InvalidOperationException e) { Console.WriteLine("Error! {0}", e.Message); } } ***** Fun with Generic Collections ***** Homer is first in line! Homer got coffee! Marge got coffee! Lisa got coffee! Error! Queue empty.
class SortPeopleByAge : IComparer⊙Swap with Generic{ public int Compare(Person firstPerson, Person secondPerson) { if (firstPerson.Age > secondPerson.Age) return 1; if (firstPerson.Age < secondPerson.Age) return -1; else return 0; } } //--------------------------------------------------------------- private static void UseSortedSet() { // Make some people with different ages. SortedSet setOfPeople = new SortedSet (new SortPeopleByAge()) { new Person {FirstName= "Homer", LastName="Simpson", Age=47}, new Person {FirstName= "Marge", LastName="Simpson", Age=45}, new Person {FirstName= "Lisa", LastName="Simpson", Age=9}, new Person {FirstName= "Bart", LastName="Simpson", Age=8} }; // Note the items are sorted by age! foreach (Person p in setOfPeople) { Console.WriteLine(p); } Console.WriteLine(); // Add a few new people, with various ages. setOfPeople.Add(new Person { FirstName = "Saku", LastName = "Jones", Age = 1 }); setOfPeople.Add(new Person { FirstName = "Mikko", LastName = "Jones", Age = 32 }); // Still sorted by age! foreach (Person p in setOfPeople) { Console.WriteLine(p); } } ***** Fun with Generic Collections ***** Name: Bart Simpson, Age: 8 Name: Lisa Simpson, Age: 9 Name: Marge Simpson, Age: 45 Name: Homer Simpson, Age: 47 Name: Saku Jones, Age: 1 Name: Bart Simpson, Age: 8 Name: Lisa Simpson, Age: 9 Name: Mikko Jones, Age: 32 Name: Marge Simpson, Age: 45 Name: Homer Simpson, Age: 47
// This method will swap any two items. // as specified by the type parameter <T>. static void Swap<T>(ref T a, ref T b) { Console.WriteLine("You sent the Swap() method a {0} typeof(T)); T temp; temp = a; a = b; b = temp; } static void Main(string[] args) { Console.WriteLine("***** Fun with Custom Generic Methods *****\n"); // Swap 2 ints. int a = 10, b = 90; Console.WriteLine("Before swap: {0}, {1}", a, b); Swap(ref a, ref b); Console.WriteLine("After swap: {0}, {1}", a, b); Console.WriteLine(); // Swap 2 strings. string s1 = "Hello", s2 = "There"; Console.WriteLine("Before swap: {0} {1}!", s1, s2); Swap (ref s1, ref s2); Console.WriteLine("After swap: {0} {1}!", s1, s2); Console.ReadLine(); }
⊙Custom Generic Structures and Classes and default keyword
// Point using ints. Pointp = new Point (10, 10); // Point using double. Point p2 = new Point (5.4, 3.3); // A generic Point structure. public struct Point { // Generic state date. private T xPos; private T yPos; // Generic constructor. public Point(T xVal, T yVal) { xPos = xVal; yPos = yVal; } // Generic properties. public T X { get { return xPos; } set { xPos = value; } } public T Y { get { return yPos; } set { yPos = value; } } public override string ToString() { return string.Format("[{0}, {1}]", xPos, yPos); } // Reset fields to the default value of the // type parameter. //**************default value************************* • Numeric values have a default value of 0. • Reference types have a default value of null. • Fields of a structure are set to 0 (for value types) or null (for reference types). public void ResetPoint() { xPos = default(T); yPos = default(T); } }
⊙Generic Base Class
•nature of the generic abstraction flows through. First, if a non-generic class extends a generic class, the derived class must specify a type parameter:
// Assume you have created a custom // generic list class. public class MyList<T> { private List<T> listOfData = new List<T>(); } // Non-generic classes must specify the type // parameter when deriving from a // generic base class. public class MyStringList : MyList<string> {}
•if the generic base class defines generic virtual or abstract methods, the derived type must override the generic methods using the specified type parameter:
// A generic class with a virtual method. public class MyList<T> { private List<T> listOfData = new List<T>(); public virtual void Insert(T data) { } } public class MyStringList : MyList<string> { // Must substitute the type parameter used in the // parent class in derived methods. public override void Insert(string data) { } }•if the derived type is generic as well, the child class can (optionally) reuse the type placeholder in its definition. However, be aware that any constraints (see next section) placed on the base class must be honored by the derived type, as in this example:
// Note that we now have a default constructor constraint (see next section). public class MyList<T> where T : new() { private List<T> listOfData = new List<T>(); public virtual void Insert(T data) { } } // Derived type must honor constraints. public class MyReadOnlyList<T> : MyList<T> where T : new() { public override void Insert(T data) { } }
Constraining Type Parameters
where T : struct | The type parameter <T> must have System.ValueType in its chain of inheritance; in other words, <T> must be a structure. |
where T : class | The type parameter <T> must not have System.ValueType in its chain of inheritance (e.g., <T> must be a reference type). |
where T : new() | The type parameter <T> must have a default constructor. This is helpful if your generic type must create an instance of the type parameter because you cannot assume you know the format of custom constructors. Note that this constraint must be listed last on a multiconstrained type. |
where T : NameOfBaseClass | The type parameter <T> must be derived from the class specified by NameOfBaseClass. |
where T : NameOfInterface | The type parameter <T> must implement the interface specified by NameOfInterface. You can separate multiple interfaces as a comma-delimited list. |