2. Generics Prepared By : Abed ElAzeem Bukhari What’s in This Chapter? ➤ An overview of generics ➤ Creating generic classes ➤ Features of generic classes ➤ Generic interfaces ➤ Generic structs ➤ Generic methods
3.
4. Performance var list = new ArrayList(); list.Add(44); // boxing — convert a value type to a reference type int i1 = (int)list[0]; // unboxing — convert a reference type to // a value type foreach (int i2 in list) { Console.WriteLine(i2); // unboxing } var list = new List < int > (); list.Add(44); // no boxing — value types are stored in the List < int > int i1 = list[0]; // no unboxing, no cast needed foreach (int i2 in list) { Console.WriteLine(i2); }
5. Type safety var list = new ArrayList(); list.Add(44); list.Add("mystring"); list.Add(new MyClass()); foreach (int i in list) { Console.WriteLine(i); } var list = new List<int>(); list.Add(44); list.Add("mystring"); // compile time error list.Add(new MyClass()); // compile time error
6. binary Code reuse var list = new List<int>(); list.Add(44); var stringList = new List<string>(); stringList.Add("mystring"); var myClassList = new List<MyClass>(); myClassList.Add(new MyClass());
7. Code bloat when the generic classes are compiled by the JIT compiler to native code, a new class for every specific value type is created. Reference types share all the same implementation of the same native class. This is because with reference types, only a 4-byte memory address (with 32-bit systems) is needed within the generic instantiated class to reference a reference type.
13. inheritance public class LinkedList<T>: IEnumerable<T> { //... A generic type can implement a generic interface. The same is possible by deriving from a class. A generic class can be derived from a generic base class: public class Base<T> { } public class Derived<T>: Base<T> { } The requirement is that the generic types of the interface must be repeated, or the type of the base class must be specified, as in this case: public class Base<T> { } public class Derived<T>: Base<string> { }
14. Inheritance cont. public abstract class Calc<T> { public abstract T Add(T x, T y); public abstract T Sub(T x, T y); } public class IntCalc: Calc<int> { public override int Add(int x, int y) { return x + y; } public override int Sub(int x, int y) { return x — y; } }
15. static members public class StaticDemo<T> { public static int x; } StaticDemo<string>.x = 4; StaticDemo<int>.x = 5; Console.WriteLine(StaticDemo<string>.x); // writes 4
16. Generic interfaces public interface IComparable<in T> { int CompareTo(T other); } The older, non-generic IComparable interface requires an object with the CompareTo() method. This requires a cast to specific types, such as to the Person class for using the LastName property: public class Person: IComparable { public int CompareTo(object obj) { Person other = obj as Person; return this.lastname.CompareTo(other.LastName); } // When implementing the generic version, it is no longer necessary to cast the object to a Person: public class Person: IComparable<Person> { public int CompareTo(Person other) { return this.LastName.CompareTo(other.LastName); } //...
20. generic methods The method Swap<T>() defines T as a generic type that is used for two arguments and a variable temp: void Swap<T>(ref T x, ref T y) { T temp; temp = x; x = y; y = temp; } A generic method can be invoked by assigning the generic type with the method call: int i = 4; int j = 5; Swap<int>(ref i, ref j); However, because the C# compiler can get the type of the parameters by calling the Swap() method, it is not required to assign the generic type with the method call. The generic method can be invoked as simply as non-generic methods: int i = 4; int j = 5; Swap(ref i, ref j);
21. generic methods example GenericMethods/Account.cs GenericMethods/Program.cs GenericMethods/Algorithm.cs
22. generic methods with Constraints GenericMethods/Algorithm.cs GenericMethods/Account.cs GenericMethods/IAccount.cs GenericMethods/Program.cs
//LinkedListObjects/LinkedListNode.cs namespace Najah.ILoveCsharp.Generics { public class LinkedListNode { public LinkedListNode(object value) { this.Value = value; } public object Value { get; private set; } public LinkedListNode Next { get; internal set; } public LinkedListNode Prev { get; internal set; } } } //LinkedListObjects/LinkedList.cs using System.Collections; namespace Najah.ILoveCsharp.Generics { public class LinkedList : IEnumerable { public LinkedListNode First { get; private set; } public LinkedListNode Last { get; private set; } public LinkedListNode AddLast(object node) { var newNode = new LinkedListNode(node); if (First == null) { First = newNode; Last = First; } else { Last.Next = newNode; Last = newNode; } return newNode; } public IEnumerator GetEnumerator() { LinkedListNode current = First; while (current != null) { yield return current.Value; current = current.Next; } } } } //LinkedListObjects/Program.cs using System; namespace Najah.ILoveCsharp.Generics { class Program { static void Main() { var list1 = new LinkedList(); list1.AddLast(2); list1.AddLast(4); list1.AddLast(&quot;6&quot;); foreach (int i in list1) { Console.WriteLine(i); } } } } //LinkedListSample/LinkedListNode.cs namespace Najah.ILoveCsharp.Generics { public class LinkedListNode<T> { public LinkedListNode(T value) { this.Value = value; } public T Value { get; private set; } public LinkedListNode<T> Next { get; internal set; } public LinkedListNode<T> Prev { get; internal set; } } } //LinkedListSample/LinkedList.cs using System.Collections; using System.Collections.Generic; namespace Najah.ILoveCsharp.Generics { public class LinkedList<T> : IEnumerable<T> { public LinkedListNode<T> First { get; private set; } public LinkedListNode<T> Last { get; private set; } public LinkedListNode<T> AddLast(T node) { var newNode = new LinkedListNode<T>(node); if (First == null) { First = newNode; Last = First; } else { Last.Next = newNode; Last = newNode; } return newNode; } public IEnumerator<T> GetEnumerator() { LinkedListNode<T> current = First; while (current != null) { yield return current.Value; current = current.Next; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } //LinkedListSample/Program.cs using System; namespace Najah.ILoveCsharp.Generics { class Program { static void Main() { var list2 = new LinkedList<int>(); list2.AddLast(1); list2.AddLast(3); list2.AddLast(5); foreach (int i in list2) { Console.WriteLine(i); } var list3 = new LinkedList<string>(); list3.AddLast(&quot;2&quot;); list3.AddLast(&quot;four&quot;); list3.AddLast(&quot;foo&quot;); foreach (string s in list3) { Console.WriteLine(s); } } } }
// DocumentManager/DocumentManager.cs using System; using System.Collections.Generic; namespace Najah.ILoveCsharp.Generics { public class DocumentManager<TDocument> where TDocument : IDocument { private readonly Queue<TDocument> documentQueue = new Queue<TDocument>(); public void AddDocument(TDocument doc) { lock (this) { documentQueue.Enqueue(doc); } } public bool IsDocumentAvailable { get { return documentQueue.Count > 0; } } public void DisplayAllDocuments() { foreach (TDocument doc in documentQueue) { Console.WriteLine(doc.Title); } } public TDocument GetDocument() { TDocument doc = default(TDocument); lock (this) { doc = documentQueue.Dequeue(); } return doc; } } }
//DocumentManager/Document.cs namespace Najah.ILoveCsharp.Generics { public interface IDocument { string Title { get; set; } string Content { get; set; } } public class Document : IDocument { public Document() { } public Document(string title, string content) { this.Title = title; this.Content = content; } public string Title { get; set; } public string Content { get; set; } } } //DocumentManager/DocumentManager.cs using System; using System.Collections.Generic; namespace Najah.ILoveCsharp.Generics { public class DocumentManager<TDocument> where TDocument : IDocument { private readonly Queue<TDocument> documentQueue = new Queue<TDocument>(); public void AddDocument(TDocument doc) { lock (this) { documentQueue.Enqueue(doc); } } public bool IsDocumentAvailable { get { return documentQueue.Count > 0; } } public void DisplayAllDocuments() { foreach (TDocument doc in documentQueue) { Console.WriteLine(doc.Title); } } public TDocument GetDocument() { TDocument doc = default(TDocument); lock (this) { doc = documentQueue.Dequeue(); } return doc; } } } //DocumentManager/Program.cs using System; namespace Najah.ILoveCsharp.Generics { class Program { static void Main() { var dm = new DocumentManager<Document>(); dm.AddDocument(new Document(&quot;Title A&quot;, &quot;Sample A&quot;)); dm.AddDocument(new Document(&quot;Title B&quot;, &quot;Sample B&quot;)); dm.DisplayAllDocuments(); if (dm.IsDocumentAvailable) { Document d = dm.GetDocument(); Console.WriteLine(d.Content); } } } }
//Variance/Shape.cs using System; namespace Najah.ILoveCsharp.Generics { public class Shape { public double Width { get; set; } public double Height { get; set; } public override string ToString() { return String.Format(&quot;Width: {0}, Height: {1}&quot;, Width, Height); } } } //Variance/Rectangle.cs namespace Najah.ILoveCsharp.Generics { public class Rectangle : Shape { } }
//Variance/IIndex.cs namespace Najah.ILoveCsharp.Generics { // covariant public interface IIndex<out T> { T this[int index] { get; } int Count { get; } } } //Variance/RectangleCollection.cs using System; namespace Najah.ILoveCsharp.Generics { public class RectangleCollection : IIndex<Rectangle> { private Rectangle[] data = new Rectangle[3] { new Rectangle { Height=2, Width=5}, new Rectangle { Height=3, Width=7}, new Rectangle { Height=4.5, Width=2.9} }; public static RectangleCollection GetRectangles() { return new RectangleCollection(); } public Rectangle this[int index] { get { if (index < 0 || index > data.Length) throw new ArgumentOutOfRangeException(&quot;index&quot;); return data[index]; } } public int Count { get { return data.Length; } } } } //Variance/Program.cs using System; namespace Najah.ILoveCsharp.Generics { class Program { static void Main() { IIndex<Rectangle> rectangles = RectangleCollection.GetRectangles(); IIndex<Shape> shapes = rectangles; for (int i = 0; i < shapes.Count; i++) { Console.WriteLine(shapes[i]); } IDisplay<Shape> shapeDisplay = new ShapeDisplay(); IDisplay<Rectangle> rectangleDisplay = shapeDisplay; rectangleDisplay.Show(rectangles[0]); } } }
//Variance/IDisplay.cs namespace Najah.ILoveCsharp.Generics { // contra-variant public interface IDisplay<in T> { void Show(T item); } } //Variance/ShapeDisplay.cs using System; namespace Najah.ILoveCsharp.Generics { public class ShapeDisplay : IDisplay<Shape> { public void Show(Shape s) { Console.WriteLine(&quot;{0} Width: {1}, Height: {2}&quot;, s.GetType().Name, s.Width, s.Height); } } } //Variance/Program.cs using System; namespace Najah.ILoveCsharp.Generics { class Program { static void Main() { IIndex<Rectangle> rectangles = RectangleCollection.GetRectangles(); IIndex<Shape> shapes = rectangles; for (int i = 0; i < shapes.Count; i++) { Console.WriteLine(shapes[i]); } IDisplay<Shape> shapeDisplay = new ShapeDisplay(); IDisplay<Rectangle> rectangleDisplay = shapeDisplay; rectangleDisplay.Show(rectangles[0]); } } }
// GenericMethods/Account.cs using System; namespace Najah.ILoveCsharp.Generics { public interface IAccount { decimal Balance { get; } string Name { get; } } public class Account : IAccount { public string Name { get; private set; } public decimal Balance { get; private set; } public Account(string name, Decimal balance) { this.Name = name; this.Balance = balance; } } } //GenericMethods/Program.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Najah.ILoveCsharp.Generics { class Program { static void Main() { var accounts = new List<Account>() { new Account(&quot;Christian&quot;, 1500), new Account(&quot;Stephanie&quot;, 2200), new Account(&quot;Angela&quot;, 1800) }; decimal amount = Algorithm.AccumulateSimple(accounts); amount = Algorithm.Accumulate(accounts); amount = Algorithm.Accumulate<Account, decimal>(accounts, (item, sum) => sum += item.Balance); } } } //GenericMethods/Algorithm.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Najah.ILoveCsharp.Generics { public static class Algorithm { public static decimal AccumulateSimple(IEnumerable<Account> source) { decimal sum = 0; foreach (Account a in source) { sum += a.Balance; } return sum; } public static decimal Accumulate<TAccount>(IEnumerable<TAccount> source) where TAccount : IAccount { decimal sum = 0; foreach (TAccount a in source) { sum += a.Balance; } return sum; } public static T2 Accumulate<T1, T2>(IEnumerable<T1> source, Func<T1, T2, T2> action) { T2 sum = default(T2); foreach (T1 item in source) { sum = action(item, sum); } return sum; } } }
//GenericMethods/Algorithm.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Najah.ILoveCsharp.Generics { public static class Algorithm { public static decimal AccumulateSimple(IEnumerable<Account> source) { decimal sum = 0; foreach (Account a in source) { sum += a.Balance; } return sum; } public static decimal Accumulate<TAccount>(IEnumerable<TAccount> source) where TAccount : IAccount { decimal sum = 0; foreach (TAccount a in source) { sum += a.Balance; } return sum; } public static T2 Accumulate<T1, T2>(IEnumerable<T1> source, Func<T1, T2, T2> action) { T2 sum = default(T2); foreach (T1 item in source) { sum = action(item, sum); } return sum; } } } //GenericMethods/Account.cs using System; namespace Najah.ILoveCsharp.Generics { public interface IAccount { decimal Balance { get; } string Name { get; } } public class Account : IAccount { public string Name { get; private set; } public decimal Balance { get; private set; } public Account(string name, Decimal balance) { this.Name = name; this.Balance = balance; } } } //GenericMethods/Program.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Najah.ILoveCsharp.Generics { class Program { static void Main() { var accounts = new List<Account>() { new Account(&quot;Christian&quot;, 1500), new Account(&quot;Stephanie&quot;, 2200), new Account(&quot;Angela&quot;, 1800) }; decimal amount = Algorithm.AccumulateSimple(accounts); amount = Algorithm.Accumulate(accounts); amount = Algorithm.Accumulate<Account, decimal>(accounts, (item, sum) => sum += item.Balance); } } }