C# machine model Programming Language Concepts and Implementation Fall 2011, Lecture 2 Reference types vs. value types Structs 2-dimensional arrays Overview Method calls: call-by-value vs. call-by-reference Other C# constructs - operator overloading - extension methods 2
Java: Primitive types vs. object types Java primitive types - int, bool, char, etc - Cannot be null Java object types - Object, String, arrays etc - Wrapper types: Integer, Boolean, Character, etc - All user defined types - Object types inherit from class Object - Null is a value in any object type 3 C#: Reference types vs. value types Value types in C# - Simple types: int, bool, char, etc - Also user defined structs - Value types inherit from class ValueType Reference types in C# - All other types - All user defined classes - Inherit from class Object - Null is a value of any reference type 4
Top layers of the type hierarchy Class Object Class String Class Array Class ValueType Class Delegate other classes array types delegate types Class Enum simple types enum types struct types 5 Machine model Objects in heap, structs and values on stack Stack Heap M2 frame M2 frame M2 frame 14 0 0 M1 frame arr1 arr2 x y 0 1 void M1() { int[] arr1 = new int[3]; var arr2 = arr1; int x = 0; int y = x; y = 1; 6
Machine model Memory divided into heap and stack - Method invocations allocate space for local variables on stack. This is called a stack frame - Space allocated on the heap has longer lifetime and is deallocated using garbage collection - Variables of value types allocated directly in stack - Variables of reference types are allocated in heap, only reference left on stack - When method invocations return the stack frame space is deallocated 7 Structs: User defined value types public class Point { protected internal int x, y; public Point(int x, int y) { this.x = x; this.y = y; public void Move(int dx, int dy) { x += dx; y += dy; public override String ToString() { return "(" + x + ", " + y + ")"; public struct SPoint { internal int x, y; public SPoint(int x, int y) { this.x = x; this.y = y; public SPoint Move(int dx, int dy) { x += dx; y += dy; return this; public override String ToString() { return "(" + x + ", " + y + ")"; 8
Example 71 public static void M1() { Point p = new Point(11, 111), q = new Point(22, 222); p = q; p.x = 33; SPoint r = new SPoint(44, 444), s = new SPoint(55, 555); r = s; r.x = 66; int[] iarr1 = new int[4]; int[] iarr2 = iarr1; iarr1[0] = 77; SPoint[] sarr = new SPoint[3]; sarr[0].x = 88; Console.WriteLine("q.x={0 s.x={1 iarr2[0]={2", p.x, s.x, iarr2[0]); M2(2); 9 Memory model Example 71 Stack Heap M2 frame i 0 M2 frame i 1 M2 frame i sarr iarr2 2 x y 0 1 2 88 x 0 x 0 y 0 y 0 0 iarr1 s x 55 0 1 2 3 77 0 0 0 y 555 r q x y 66 555 : Point x 11 y 111 : Point x 33 y 222 M1 frame p 10
Struct benefits - Code often clearer using structs - Read and write performance better When to use structs On the other hand - Copying (big) structs is expensive - This means method calls using structs can be expensive (can call by reference) Structs cannot inherit but can implement interfaces 11 Boxing and unboxing Assignment of struct to object creates a copy on heap. This is called boxing SPoint p = new SPoint(11, 22); // Create a struct value in p SPoint[] arr = { p, p ; // Two more copies of p arr[0].x = 33; Console.WriteLine(arr[0] + " " + arr[1]); // Prints (33, 22) (11, 22) Object o = p; // Another copy of p, in heap p.x = 44; Console.WriteLine(p + " " + o); // Prints (44, 22) (11, 22) Console.WriteLine(o is SPoint); // Prints True Console.WriteLine(o is int); // Prints False 12
Two dimensional arrays A Java 2D array is an array of arrays C# also has proper 2D arrays. These are less flexible but faster to access Java/C# double[][] an array of arrays C# double[,] a rectangular array 13 Example 38 // Rectangular array creation double[,] r1 = { { 0.0, 0.1, { 1.0, 1.1, { 2.0, 2.1 ; double[,] r2 = new double[3,2]; for (int i=0; i<3; i++) for (int j=0; j<2; j++) r2[i,j] = i + 0.1 * j; // Jagged array creation double[] row0 = { 0.0, row1 = { 1.0, 1.1, row2 = { 2.0, 2.1, 2.2 ; double[][] t1 = { row0, row1, row2 ; double[][] t2 = { new double[] {0.0, new double[] {1.0, 1.1, new double[] {2.0, 2.1, 2.2; double[][] t3 = new double[3][]; // Create first dimension array for (int i=0; i<3; i++) { t3[i] = new double[i+1]; for (int j=0; j<=i; j++) t3[i][j] = i + 0.1 * j; // Create second dimension arrays // double[][] t4 = new double[3][3]; // Illegal array creation 14
Method calls public static void Swap(int x, int y) { int tmp = x; x = y; y = tmp; What do these methods do? public static void Swap(int[] x, int[] y) { int[] tmp = x; x = y; y = tmp; public static void Swap(int[] arr, int x, int y) { int tmp = arr[x]; arr[x] = arr[y]; arr[y] = tmp; public static void Main() { int a = 1, b = 2; Swap(a,b); Console.WriteLine("a = {0, b = {1", a, b); int[] myarr = new int[3], myarr2 = new int[3]; myarr2[0] = 1; Swap(myArr, myarr2); Console.WriteLine("myArr[0] = {0, myarr2[0] = {1", myarr[0], myarr2[0]); int[] myarr3 = new int[2]; myarr3[1] = 1; Swap(myArr3, 0, 1); Console.WriteLine("myArr3[0] = {0, myarr3[1] = {1", myarr3[0], myarr3[1]); 16
public static void Swap(int x, int y) { int tmp = x; x = y; y = tmp; What do these methods do Formal parameter public static void Swap(int[] x, int[] y) { int[] tmp = x; x = y; y = tmp; public static void Swap(int[] arr, int x, int y) { int tmp = arr[x]; arr[x] = arr[y]; arr[y] = tmp; public static void Main() { Actual int a = 1, b = 2; parameter Swap(a,b); Console.WriteLine("a = {0, b = {1", a, b); int[] myarr = new int[3], myarr2 = new int[3]; myarr2[0] = 1; Swap(myArr, myarr2); Console.WriteLine("myArr[0] = {0, myarr2[0] = {1", myarr[0], myarr2[0]); int[] myarr3 = new int[2]; myarr3[1] = 1; Swap(myArr3, 0, 1); Console.WriteLine("myArr3[0] = {0, myarr3[1] = {1", myarr3[0], myarr3[1]); 17 Method calls in C# and Java are by value First actual parameters are evaluated Method calls Then their values are copied to called methods stack frame In case of reference types, the value is a pointer Exercise: Draw the heap and stack as it develops during execution of method Main slide 16 18
Example: method calls with structs public static SPoint Reflect(SPoint p) { return new SPoint(p.y, p.x); SPoint p = new SPoint(0,1); SPoint q = Reflect(p); Console.WriteLine(q); 19 C# also allows call by reference Call by reference in C# public static void Swap(ref int x, ref int y) { int tmp = x; x = y; y = tmp; int a = 0; int b = 1; Swap(ref a, ref b); Console.WriteLine("a = {0, b = {1", a, b); public static void Reflect(ref SPoint p) { int tmp = p.x; p.x = p.y; p.y = tmp; p = new SPoint(0,1); Reflect(ref p); Console.WriteLine(p); 20
Method calls in call-by-reference First actual parameters are evaluated Then pointers to their values are copied to called methods stack frame This allows for more efficient manipulations of values without copying 21 Example 94 double d1 = 1.1, d2 = 2.2; int[] a1 = new int[4], a2 = new int[4]; M(d1, ref d2, a1, ref a2); static void M(double dd1, ref double dd2, int[] aa1, ref int[] aa2) { dd1 = 3.3; dd2 = 4.4; aa1[0] = 17; aa2[0] = 18; aa2 = new int[3]; aa1 = aa2; dd1 In M: 3.3 d1 In Main: 1.1 Heap: dd2 d2 4.4 aa1 a1 17 0 0 0 aa2 a2 18 0 0 0 0 0 0 22
Call of instance methods Call of instance methods on structs pass struct by reference public struct SPoint { internal int x, y; public SPoint(int x, int y) { this.x = x; this.y = y; public void Reflect() { int tmp = x; x = y; y = tmp public override String ToString() { return "(" + x + ", " + y + ")"; SPoint p = new SPoint(0,1); p.reflect(); // Changes p 23 Garbage collection Consider the following degenerate code public void M() { Object o = new Object(); Object o is allocated in heap But when M returns the only reference to it is deallocated So then object o just wastes space in memory Run-time system garbage collects o 24
Other C# constructions Operator overloading Operators ==,!=, +, *, can be overloaded An operator is a public static method Works well with struct types struct Frac : IComparable { public readonly long n, d; // NB: Meaningful only if d!=0 public static Frac operator+(frac r1, Frac r2) { return new Frac(r1.n*r2.d+r2.n*r1.d, r1.d*r2.d); public static Frac operator*(frac r1, Frac r2) { return new Frac(r1.n*r2.n, r1.d*r2.d); public static bool operator==(frac r1, Frac r2) { return r1.n==r2.n && r1.d==r2.d; public static bool operator!=(frac r1, Frac r2) { return r1.n!=r2.n r1.d!=r2.d; Frac x = new Frac(2,3), y = new Frac(3,5), z = new Frac(28,17); x+y*z == z*y+x 26
User defined conversions Special methods that transform values Implicit (automatic), explicit (by cast) struct Frac : IComparable { // Implicit conversion from int to Frac: public static implicit operator Frac(int n) { return new Frac(n, 1); // Explicit conversion from Frac to double: public static explicit operator double(frac r) { return ((double)r.n)/r.d; Frac x = 1; double y = double(x); Frac z = x + 14; 27 Can be added to existing type Are defined separately in a static class static class BoolExtensions { public static void Print(this bool b) { Console.WriteLine(b? ja : nej ); Are called like instance methods (1 == 1).Print(); Extension methods No instances allowed 28
Extension methods as syntactic sugar A call to extension method public static void M(this C x, int y) { o.m(42); Is just syntactic sugar for M(o, 42); Not the same as an instance method call e.g. extension methods may not refer to private fields Also subtleties on structs Extension methods are always non-virtual 29 Week numbers (1-53) on DateTime Let s teach.net ISO week numbers static class DateTimeExtensions { public static int IsoWeek (this DateTime dt) { Implementation omitted Use it like any System.DateTime method int thisweek = DateTime.Today.IsoWeek(); 30
Extension methods on interfaces public static bool IsSorted(this IEnumerable<T> xs) where T : IComparable<T> { var etor = xs.getenumerator(); if (etor.movenext()) { T prev = etor.current; while (etor.movenext()) if (prev.compareto(etor.current) > 0) return false else prev = etor.current; return true; Only for data that have CompareTo() double[] darr = {4.5, 1.6, 5.6, 7.9 ; Console.WriteLine(darr.IsSorted()); 31 Extension methods on structs An instance method on a struct passes the struct by reference, so that fields can be updated An extension method call passes it by value So an extension method is not an instance method struct MyStruct { internal int x; public void RealIncrement() { x++; By value: Pass a copy of struct static class MyStructExtensions { public static void UselessIncrement(this mystruct b) { b.x++; No effect on b.x b.uselessincrement(); 32
This weeks intended learning outcomes After this week you should be able to - Explain difference between reference types and value types - Draw machine model and use it to explain program behaviour Explain assignments in machine model Explain method calls in machine model - Create and use structs - Create and use call-by-reference methods - Create and use extension methods and overloaded operators 33