본문 바로가기

Software/C#.Net

Generics

◎Two Main Issue for non-Generic Types
   ▶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.

⊙Sort an array by Sort<T>()
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 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 
⊙Example Stack<T> 
static void UseGenericStack() 
{ 
  Stack 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. 
⊙Example Queue<T> 
static void GetCoffee(Person p) 
{ 
  Console.WriteLine("{0} got coffee!", p.FirstName); 
} 
static void UseGenericQueue() 
{ 
  // Make a Q with three people. 
  Queue 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. 
⊙Example SortedSet<T>  
class SortPeopleByAge : IComparer 
{ 
  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 
⊙Swap with Generic
// 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. 
Point p = 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.