19 C#2
19 C#2
• Generic Types
• Iterators
• Simplified Delegates
• Anonymous Methods
• Partial Types
• Various
2
Problems Without Generic Types
Assume we need a class that can work with arbitrary objects
class Buffer {
private object[] data;
public void Put(object x) {...}
public object Get() {...}
}
Problems
• Boxing and type casts needed
buffer.Put(3); // boxing imposes run-time costs
int x = (int)buffer.Get(); // type cast imposes run-time costs
• One cannot statically enforce homogeneous data structures
buffer.Put(3); buffer.Put(new Rectangle());
Rectangle r = (Rectangle)buffer.Get(); // can result in a run-time error!
• Special types IntBuffer, RectangleBuffer, ... introduce redundancy
3
Generic Class Buffer
generic type type parameter (placeholder for some real type)
class Buffer<Element> {
• works also for structs and interfaces
private Element[] data;
public Buffer(int size) {...} • placeholder type Element can be used
public void Put(Element x) {...} like a normal type
public Element Get() {...}
}
Usage (instantiation)
Buffer<int> a = new Buffer<int>(100);
a.Put(3); // accepts only int parameters; no boxing
int i = a.Get(); // no type cast needed!
Benefits
• enforces a homogeneous data structure with compile-time type checking
• efficient (no boxing, no type casts)
Usage
Buffer<int, int> a = new Buffer<int, int>();
a.Put(100, 0);
int elem, prio;
a.Get(out elem, out prio);
5
Constraints
Constraints about placeholder types are specified as base types
interface or base class
class OrderedBuffer <Element, Priority> where Priority: IComparable {
Element[] data;
Priority[] prio;
int lastElem;
...
public void Put(Element x, Priority p) {
int i = lastElement;
while (i >= 0 && p.CompareTo(prio[i]) > 0) {data[i+1] = data[i]; prio[i+1] = prio[i]; i--;}
data[i+1] = x; prio[i+1] = p;
}
}
Usage
OrderedBuffer<int, int> a = new OrderedBuffer<int, int>();
a.Put(100, 3);
7
Constructor Constraint
For creating objects of a generic types
class Stack<T, E> where E: Exception, new() { specifies that the placeholder E
T[] data = ...;
must have a parameterless constructor.
int top = -1;
public void Push(T x) {
if (top >= data.Length - 1)
throw new E();
else
data[++top] = x;
}
}
Usage
class MyException: Exception {
public MyException(): base("stack overflow or underflow") {}
}
9
Assignment Compatibility
Compatibiliy between T<x> and a non-generic base class
A
class A {...}
class B<X>: A {...}
class C<X,Y>: A {...} B<X> C<X,Y> ...
10
Overriding Methods
class Buffer<Element> {
...
public virtual void Put(Element x) {...}
}
if (obj is Buffer<int>)
buf = (Buffer<int>) obj;
Type t = typeof(Buffer<int>);
Console.WriteLine(t.Name); // =>
Buffer[System.Int32]
Reflection yields also the concrete types substituted for the placeholders!
12
Generic Methods
Methods that can work with arbitrary data types
static void Sort<T> (T[] a) where T: IComparable { can sort any array as long as the
for (int i = 0; i < a.Length-1; i++) { array elements implement IComparable
for (int j = i+1; j < a.Length; j++) {
if (a[j].CompareTo(a[i]) < 0) {
T x = a[i]; a[i] = a[j]; a[j] = x;
}
}
}
}
Usage
int[] a = {3, 7, 2, 5, 3}; string[] s = {"one", "two", "three"};
... ...
Sort<int>(a); // a == {2, 3, 3, 5, 7} Sort<string>(s); // s == {"one", "three", "two"}
From the method parameters the compiler can usually infer the concrete type that is to
be substituted for the placeholder type; so one can simply write:
Sort(a); // a == {2, 3, 3, 5, 7} Sort(s); // s == {"one", "three", "two"}
13
Generic Delegates
delegate bool Match<T>(T value);
class Payment {
public DateTime date;
public int amount;
}
class Account {
ArrayList payments = new ArrayList();
public void Add(Payment p) { payments.Add(p); } A method is passed,
which checks for every payment,
public int AmountPayed(Match<Payment> match) { whether it matches a certain criterion
int val = 0;
foreach (Payment p in payments)
if (match(p)) val += p.amount;
return val;
}
}
bool PaymentsAfter(Payment p) {
return DateTime.Compare(p.date, myDate) >= 0;
}
...
myDate = new DateTime(2001, 11, 9);
int val = account.AmountPayed(new Match<Payment>(PaymentsAfter));
14
Null Values
Setting a value to null
void Foo<T>() {
T x = null; // error
T y = 0; // error
T z = default(T); // ok! 0, '\0', false, null
}
15
Namespace System.Collections.Generic
New generic types
Classes
List<T> corresponds to ArrayList
Dictionary<K, V> corresponds to Hashtable
SortedList<K, V>
Stack<T>
Queue<T>
Interfaces
ICollection<T>
IList<T>
IDictionary<K, V>
IEnumerable<T>
IEnumerator<T>
IComparable<T>
IComparer<T>
16
What Happens Behind the Scene?
class Buffer<Element> {...} Compiler generates CIL code for class Buffer
with a placeholder for Element.
Buffer<int> a = new Buffer<int>(); The JIT compiler generates a new class Buffer<int>
in which Element is replaced with int.
Buffer<float> c = new Buffer<float>(); The JIT compiler generates a new class Buffer<float>
in which Element is replaced with float.
Buffer<string> a = new Buffer<string>(); The JIT compiler generates a new class Buffer<object>
which can work with all reference types.
18
Iterators
19
Iterators so far
foreach loop can be applied to objects of classes which implement IEnumerable
class MyClass: IEnumerable { interface IEnumerable {
... IEnumerator GetEnumerator();
public IEnumerator GetEnumerator() { }
return new MyEnumerator(...);
}
complicated to implement!!
20
Iterator Methods
class MyClass { Characteristics of an interator method
string first = "first";
• has the signature
string second = "second";
public IEnumerator GetEnumerator
string third = "third";
... • statement body contains at least
public IEnumerator GetEnumerator() { one yield statement
yield return first;
yield return second;
yield return third;
}
}
Note
• MyClass need not implement IEnumerable!
• Instead of IEnumerator it is better to use IEnumerator<string> (avoids a type cast)
• IEnumerator<T> is in System.Collections.Generic
21
What Happens Behind the Scene?
returns an object of the following
public IEnumerator<int> GetEnumerator() { compiler-generated class
try {
... class _Enumerator : IEnumerator<int> {
} finally { int Current { get {...} }
... bool MoveNext() {...}
} void Dispose() {...}
} }
is translated into
foreach (int x in list) IEnumerator<int> _e = list.GetEnumerator();
Console.WriteLine(x); try {
while (_e.MoveNext())
Console.WriteLine(_e.Current);
} finally {
if (_e != null) _e.Dispose();
}
yield return expr; • yields a value for the next iteration of the foreach loop
• type of expr must be compatible with
-T (if IEnumerator<T>)
- object (if IEnumerator)
23
Specific Iterators
class MyList {
int[] data = ...;
public IEnumerator<int> GetEnumerator() { Standard iterator
for (int i = 0; i < data.Length; i++)
yield return data[i];
}
public IEnumerable<int> Range(int from, int to) { Specific iterator as a method
if (to > data.Length) to = data.Length; • arbitrary name and parameter list
for (int i = from; i < to; i++) • result type IEnumerable or IEnumerable<T>
yield return data[i];
• must contain a yield statement
}
public IEnumerable<int> Downwards { Specific iterator as a property
get { • arbitrary name
for (int i = data.Length - 1; i >= 0; i--) • result type IEnumerable or IEnumerable<T>
yield return data[i];
• must contain a yield statement
}
}
}
is translated into
foreach (int x in list.Range(2, 7)) IEnumerator<int> _e =
Console.WriteLine(x); list.Range(2, 7).GetEnumerator();
try {
while (_e.MoveNext())
Console.WriteLine(_e.Current);
} finally {
if (_e != null) _e.Dispose();
}
25
Example: Iterating Over a Tree
class Tree {
Node root = null;
26
Simplified Delegates
27
Simplified Creation of Delegates
delegate void Printer(string s);
void Foo(string s) {
Console.WriteLine(s);
}
Printer print;
print = new Printer(this.Foo);
print = this.Foo; simplified form:
print = Foo; delegate type is infered from the type of the left-hand side
overloading is resolved
Printer print = Foo; assigns Foo(string s)
using the type of the
Function square = Foo; assigns Foo(double x)
left-hand side
28
Anonymous Methods
29
Ordinary Delegates
delegate void Visitor(Node p); class C {
int sum = 0;
class List {
Node[] data = ...; void SumUp(Node p) { sum += p.value; }
... void Print(Node p) { Console.WriteLine(p.value); }
public void ForAll(Visitor visit) { void Foo() {
for (int i = 0; i < data.Length; i++) List list = new List();
visit(data[i]); list.ForAll(SumUp);
} list.ForAll(Print);
} }
}
30
Anonymous Methods
class List {
delegate void Visitor(Node p); ...
public void ForAll(Visitor visit) {
class C { ...
}
void Foo() { }
List list = new List();
int sum = 0;
list.ForAll(delegate (Node p) { Console.WriteLine(p.value); });
list.ForAll(delegate (Node p) { sum += p.value; });
}
} formal parameter code
Restrictions
• anonymous methods must not have formal parameters of the kind params T[]
• anonymous methods must not be assigned to object
• anonymous methods must not access ref or out parameters of the enclosing method
31
Further Simplification
delegate void EventHandler (object sender, EventArgs arg);
Formal parameters can be omitted if they are not used in the method body
Restriction
• Formal parameters can only be omitted if the delegate type does not have out parameters
32
Outer Variables
If anonymous methods access variables of the enclosing method
these variables are evacuated into a dummy object (capturing).
class Test {
static Adder CreateAdder() {
int x = 0; 2
3
10
dummy object
return delegate { x++; return x; }; add
}
delegate x++; return x;
static void Main() {
Adder add = CreateAdder();
Console.WriteLine(add());
Console.WriteLine(add()); The dummy object lives as long as the delegate object
Console.WriteLine(add());
}
}
Output: 1
2
3
33
Partial Types
34
Class Consisting of Multiple Parts
public partial class C { file Part1.cs
int x;
public void M1(...) {...}
public int M2(...) {...}
}
Benefit
• Parts can be grouped by functionality
• different developers can work on different parts of the class at the same time
• one part can be machine-generated, other parts can be hand-written
35
Various
36
Static Classes
May only have static fields and methods
Benefit
• shows explicitly that this class contains only static members
• the compiler can make sure that all members are declared as static
37