0% found this document useful (0 votes)
30 views37 pages

19 C#2

New features in C# 2.0 include generic types, iterators, simplified delegates, anonymous methods, and partial types. Generic types allow types to be written as placeholders that can be replaced by concrete types, enabling type-safe and efficient code. Constraints can specify requirements on placeholder types. Generic methods enable operations on arbitrary types. Delegates can be generic, and null values are handled through default(T).

Uploaded by

api-3734769
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
30 views37 pages

19 C#2

New features in C# 2.0 include generic types, iterators, simplified delegates, anonymous methods, and partial types. Generic types allow types to be written as placeholders that can be replaced by concrete types, enabling type-safe and efficient code. Constraints can specify requirements on placeholder types. Generic methods enable operations on arbitrary types. Delegates can be generic, and null values are handled through default(T).

Uploaded by

api-3734769
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 37

New Features in C# 2.

• Generic Types
• Iterators
• Simplified Delegates
• Anonymous Methods
• Partial Types
• Various

© University of Linz, Institute for System Software, 2004


published under the Microsoft Curriculum License
1
Generic Types

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!

Buffer<Rectangle> b = new Buffer<Rectangle>(100);


b.Put(new Rectangle()); // accepts only Rectangle parameters
Rectangle r = b.Get(); // no typ cast needed!

Benefits
• enforces a homogeneous data structure with compile-time type checking
• efficient (no boxing, no type casts)

Genericity is also available in Ada, Eiffel, C++ (templates), Java 5.0


4
Multiple Placeholder Types
Buffer with priorities
class Buffer <Element, Priority> {
private Element[] data;
private Priority[] prio;
public void Put(Element x, Priority prio) {...}
public void Get(out Element x, out Priority prio) {...}
}

Usage
Buffer<int, int> a = new Buffer<int, int>();
a.Put(100, 0);
int elem, prio;
a.Get(out elem, out prio);

Buffer<Rectangle, double> b = new Buffer<Rectangle, double>();


b.Put(new Rectangle(), 0.5);
Rectangle r; double prio;
b.Get(out r, 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;
}
}

Allows operations on instances of placeholder types

Usage
OrderedBuffer<int, int> a = new OrderedBuffer<int, int>();
a.Put(100, 3);

parameter must implement IComparable


6
Multiple Constraints
class OrderedBuffer <Element, Priority>
where Element: MyClass
where Priority: IComparable
where Priority: ISerializable {
...
public void Put(Element x, Priority p) {...}
public void Get(out Element x, out Priority p) {...}
}

must be a subclass of MyClass


Usage
must implement IComparable and ISerializable
OrderedBuffer<MySubclass, MyPrio> a = new OrderedBuffer<MySubclass, MyPrio>();
...
a.Put(new MySubclass(), new MyPrio(100));

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") {}
}

Stack<int, MyException> stack = new Stack<int, MyException>();


...
stack.Push(3);
8
Genericity and Inheritance
class Buffer <Element>: List<Element> { can also implement generic interfaces
...
public void Put(Element x) {
this.Add(x); // Add is inherited from List
}
}

From which classes may a generic class be derived?

• from a non-generic class class T<X>: B {...}

• from an instantiated generic class class T<X>: B<int> {...}

• from a generic class class T<X>: B<X> {...}


with the same placeholder

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> ...

A a1 = new B<int>(); B<int> C<int,float>


A a2 = new C<int, float>(); B<float> C<float,int>
... ...

Compatibility between T<x> and a generic base class


A<X>
class A<X> {...}
class B<X>: A<X> {...}
class C<X,Y>: A<X> {...} B<X> C<X,Y> ...

A<int> a1 = new B<int>(); ok, if corresponding placeholders are replaced


A<int> a2 = new C<int, float>(); by the same type

A<int> a3 = new B<short>(); illegal

10
Overriding Methods
class Buffer<Element> {
...
public virtual void Put(Element x) {...}
}

Methods inherited from an instantiated generic class


class MyBuffer: Buffer<int> {
...
public override void Put(int x) {...} Element is replaced by the concrete type int
}

Methods inherited from a generic class


class MyBuffer<Element>: Buffer<Element> {
...
public override void Put(Element x) {...} Element remains to be a placeholder
}

The following is illegal (it is not allowed to inherit a placeholder)


class MyBuffer: Buffer<Element> {
...
public override void Put(Element x) {...}
} 11
Run-time Type Checks
Instantiated generic types can be used like non-generic types

Buffer<int> buf = new Buffer<int>(20);


object obj = buf;

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
}

Comparing a value against null


void Foo<T>(T x) { for reference types x == null does a comparison
if (x == null) { for value types x == null returns false
Console.WriteLine(true);
} else { Foo(3); // false
Console.WriteLine(false); Foo(0); // false
} Foo("Hello"); // false
} Foo<string>(null); // true

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.

Instantiation with value types

Buffer<int> a = new Buffer<int>(); The JIT compiler generates a new class Buffer<int>
in which Element is replaced with int.

Buffer<int> b = new Buffer<int>(); Uses existing Buffer<int>.

Buffer<float> c = new Buffer<float>(); The JIT compiler generates a new class Buffer<float>
in which Element is replaced with float.

Instantiation with reference types

Buffer<string> a = new Buffer<string>(); The JIT compiler generates a new class Buffer<object>
which can work with all reference types.

Buffer<string> b = new Buffer<string>(); Uses existing Buffer<object>.

Buffer<Node> b = new Buffer<Node>(); Uses existing Buffer<object>.


17
Differences to Other Languages
C++ similar Syntax
template <class Element>
class Buffer {
...
void Put(Element x);
}
Buffer<int> b1;
Buffer<int> b2;

• compiler generates a new class for every instantiation


• no constraints, less type-safe
• placeholders can also be used for constants

Java • since Version 5.0


• placeholders can only be instantiated with reference types
• implemented with type casts (imposes tun-time costs)
• generics are just in the Java language and not in the VM
• reflection does not yield exact type information about placeholders

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(...);
}

class MyEnumerator: IEnumerator {


public object Current { get {...} }
public bool MoveNext() {...}
public void Reset() {...}
}
}

MyClass x = new MyClass();


...
foreach (object obj in x) ...

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;
}
}

MyClass x = new MyClass();


How does an iterator method work?
... • returns a sequence of values
foreach (string s in x) Console.Write(s + " "); • foreach loop traverses this sequence
// prints "first second 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();
}

MoveNext runs to the next yield statement


Dispose executes a possibly existing
finally block in the iterator method
22
yield Statement
2 kinds (may only occur in iterator methods)

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)

yield break; • terminates the iteration

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
}
}
}

MyList list = new MyList();


foreach (int x in list) Console.WriteLine(x);
foreach (int x in list.Range(2, 7)) Console.WriteLine(x);
foreach (int x in list.Downwards) Console.WriteLine(x);
24
How Specific Iterators are Compiled
returns an object of the following class
public IEnumerable<int> Range(int from, int to) { class _Enumerable : IEnumerable<int> {
if (to > data.Length) to = data.Length; IEnumerator<int> GetEnumerator()
for (int i = from; i < to; i++) {...}
yield return data[i]; }
} this returns an object of the following class
class _Enumerator : IEnumerator<int> {
int from, to;
int Current { get {...} }
bool MoveNext() {...}
void Dispose() {..}
}

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;

public void Add(int val) {...}


public bool Contains(int val) {...}

public IEnumerator<int> GetEnumerator() {


return root.GetEnumerator();
}
}
Usage
class Node { ...
public int val; Tree tree = new Tree();
public Node left, right; ...
foreach (int x in tree)
public Node(int x) { val = x; } Console.WriteLine(x);

public IEnumerator<int> GetEnumerator() {


if (left != null) Creates an enumerator object for every
foreach (int x in left) yield return x;
yield return val;
node of the tree!
if (right != null)
foreach (int x in right) yield return x;
}
}

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

delegate double Function(double x);


double Foo(double x) {
return x * x;
}

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);
} }
}

• requires the declaration of a named method (SumUp, Print, ...)


• SumUp and Print cannot access the local variables of Foo
=> sum must be declared as a global field

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

• method code is specified in-place


• does not require the declaration of a named method
• anonymous method can access Foo's local variable sum
• return terminates the anonymous method (not the enclosing method)

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);

Button button = new Button();


Button.Click += delegate (object sender, EventArgs arg) { Console.WriteLine("clicked"); };

Can be simplified as follows


Button.Click += delegate { Console.WriteLine("clicked"); };

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).

delegate int Adder();

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(...) {...}
}

public partial class C { file Part2.cs


string y;
public void M3(...) {...}
public void M4(...) {...}
public void M5(...) {...}
}

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

Should only be used as an exception

35
Various

36
Static Classes
May only have static fields and methods

static class Math {


public static double Sin(double x) {...}
public static double Cos(double x) {...}
...
}

Benefit
• shows explicitly that this class contains only static members
• the compiler can make sure that all members are declared as static

Static classes must not be used as types

37

You might also like