10. #dotNETSpain2015
Compiler makes some
initial work with constants
string str = "a" + "b" + "c" + "d" + "e" + "f";
Almost 10 times faster !!!
Slower:
string str = new StringBuilder().
Append("a").Append("b").Append("c").
Append("d").Append("e").Append("f").
ToString()
12. #dotNETSpain2015
Overloading and Overriding
Parent p = new Parent(); Child c = new Child();
Parent c2 = new Child();
call(p);
call(c);
call(c2);
public class Child : Parent ...
public class Utilities {
void call(Parent o) { Console.WriteLine("Parent called"); }
void call(Child o) { Console.WriteLine("Child called"); }
13. #dotNETSpain2015
Overloading and Overriding
Polymorphism types:
Overloading (params) - compilation time - decide
method to call on input params
Overriding (inheritance) - execution - decide method to
call on instance class
Parent called
Child called
Parent called
14. #dotNETSpain2015
Overloading and Overriding
public Point(int x, int y) {...
public bool Equals(Point p) {
return x == p.x && y == p.y;
}
... GetHashCode ...
}
Point p1 = new Point(10,20);
Point p2 = new Point(10,20);
Console.WriteLine(p1.Equals(p2)); ???
HashSet<Point> set = new HashSet<Point>();
set.add(p1);
Console.WriteLine(set.Contains(p2)); ???
18. #dotNETSpain2015
String equality
Operators are static methods.
Static methods can be overloaded but not
overridden (can't be inherited).
So decision is in compilation time.
public static bool == (String left, String right)
19. #dotNETSpain2015
Inheritance override
interface Inter { void Message(); }
class A : Inter {
public void Message() { Console.WriteLine("A"); }}
class B : A {
public new void Message() { Console.WriteLine("B"); }}
B b = new B();
b.Message(); // ???
((Inter)b).Message(); // ???
((A)b).Message(); // ???
A a = b;
a.Message(); // ???
((Inter)a).Message(); // ???
20. #dotNETSpain2015
Inheritance override
VIRTUAL, OVERRIDE, SEALED, NEW
B b = new B();
b.Message(); // B
((Inter)b).Message(); // A
((A)b).Message(); // A
A a = b;
a.Message(); // A
((Inter)a).Message(); // A
TRY TO AVOID:
- Different behavior depending on the reference type
- Avoid "new" to redefine sealed methods
- Avoid explicit interface implementation
21. #dotNETSpain2015
Inheritance override
Design for inheritance or else prohibit it
public class Inheritable {
virtual public void Message() { ... }
...
sealed public class DontInherit {
public new void Message() { ... }
...
22. #dotNETSpain2015
Test data in a fast way
string[] strs = {"hello", "how", "are", "you"};
int[] ints = {10, 20, 30};
Point[] points = { new Point(10, 20), new Point(20, 40)};
List<string> list = new List<string>();
list.Add("hello");
list.Add("how");
Dictionary<string,int> dict = new Dictionary<string,int>();
dict.Add("hello", 13);
dict.Add("how", 15);
23. #dotNETSpain2015
Test data in a fast way
string[] strs = {"hello", "how", "are", "you"};
int[] ints = {10, 20, 30};
Point[] points = { new Point(10, 20), new Point(20, 40)};
If Enumerable and Add method:
var list = new List<string> {"hello", "how"};
var dict = new Dictionary<string,int> {
{ "hello", 13 },
{ "how", 15 },
};
26. #dotNETSpain2015
CLR & C# (properties vs fields)
FIELD:
public String Description;
PROPERTY:
private String comment;
public String Comment
{
get { return comment; }
set { comment = value; }
}
27. #dotNETSpain2015
CLR & C# (properties vs fields)
FIELD: Binary incompatible if later changed to property
public String Description;
public String Description { get; set; }
Implementation changes keep binary compatibility
Never user public fields
28. #dotNETSpain2015
CLR & C# (readonly vs const)
public const int DefaultPageSize = 10;
public static int DefaultPageSize2 { get { return 10; } }
public static readonly int DefaultPageSize3 = 10;
29. #dotNETSpain2015
CLR & C# (readonly vs const)
public const int DefaultPageSize = 10;
public static int DefaultPageSize2 { get { return 10; } }
public static readonly int DefaultPageSize3 = 10;
Const -> compilation-time (If assembly with const changes, all consumers
must recompile, const is not supported in CLR)
Only constant values, only simple types, ...
Readonly -> runtime
Never user public const if value could change in the future
30. #dotNETSpain2015
CLR & C# (readonly)
Can be set in constructor, can't be set after object is created
private readonly int _elementCount;
public int ElementCount { get { return _elementCount; } }
MyClass(int pages, int pageSize)
{
_elementCount = pages * pageSize;
}
THIS CAN BE CHANGED:
public int ElementCount2 { get; private set; }
31. #dotNETSpain2015
Any difference ?
public void Method1(String param1 = "hello", int param2 = 10)
{
// DO STUFF HERE
}
public void Method2() {Method2("hello");}
public void Method2(String param1) { Method2(param1, 10);}
public void Method2(String param1, int param2)
{
// DO STUFF HERE
}
CLR & C# (optional params)
32. #dotNETSpain2015
Default param values (and named params)
are in callsite assembly CIL.
If default values (or names) change in calling
site, all callsite assemblies must be
recompiled
public void Method1(String param1 = "BYE", int param2 = 10)
{
// DO STUFF HERE
}
CLR & C# (optional params)
33. #dotNETSpain2015
Pros and cons ?
Point p = new Point(x: 10, y: 20);
Point p = new Point { X=10, Y=20 };
CLR & C# (optional params
vs object initializers)
34. #dotNETSpain2015
Pros and cons ?
Point p = new Point(x: 10, y: 20);
EQUIVALENT TO:
Point p = new Point(10, 20);
Point p = new Point { X=10, Y=20 };
EQUIVALENT TO:
Point p = new Point();
p.X = 10;
p.Y = 20;
CLR & C# (optional params
vs object initializers)
35. #dotNETSpain2015
static class StringMethods {
public static bool IsLengthEven(this String str) {
return (str.Length & 1 ) == 1;
}
}
Console.WriteLine("is even hello: " + "hello".IsLengthEven());
Console.WriteLine("is even hello!: " + "hello!".IsLengthEven());
CLR & C# (extension methods)
37. #dotNETSpain2015
Value and Reference types
C# is not a PURE OO language, not
everything is an object.
Heap / Stack
Reference: class, interface, delegate, array
Value: Predefined types (except string,
object), struct, enum
39. #dotNETSpain2015
Enum
public enum Quarter
{
Q1 = 1, Q2 = 2, Q3 = 3, Q4 = 4
};
Quarter q1 = Quarter.Q1;
Quarter qa;
Quarter qb = new Quarter();
Console.WriteLine("q1: " + q1); // Q1
Console.WriteLine("qa: " + qa); // Compilation error because local var,
it would work in fields.
Console.WriteLine("qb: " + qb); // 0 Value types initialized to 0 or
default value
40. #dotNETSpain2015
Struct arrays
PointC[] c = new PointC[1000]; // Any diference ???
for (int i = 0; i < c.Length; i++)
{
c[i] = new PointC {X = i};
}
PointS[] s = new PointS[1000]; // Any diference ???
for (int i = 0; i < s.Length; i++)
{
s[i] = new PointS { X = i };
}
41. #dotNETSpain2015
Struct arrays
PointC[] c = new PointC[1000];
// HERE 1000 NULL REFERENCES ARE CREATED
for (int i = 0; i < c.Length; i++) {
c[i] = new PointC {X = i};
}
PointS[] s = new PointS[1000];
// HERE 1000 STRUTS WITH X,Y=0 ARE CREATED
for (int i = 0; i < s.Length; i++) {
s[i] = new PointS { X = i };
}
42. #dotNETSpain2015
Struct constructor
struct NegativePoint {
public NegativePoint(int x, int y) : this() { X = x; Y = y; }
private int x; private int y;
public int X {
set
{
if (value >= 0) throw
new ArgumentOutOfRangeException("X must be negative");
x = value;
}
get { return x; }
...
NegativePoint pa = new NegativePoint(0, 0); /// ???
NegativePoint pb = new NegativePoint(-5,-10); /// Can we enforce negative X,Y
?
43. #dotNETSpain2015
Struct constructor
Default public constructor always exists in structs
NegativePoint pa = new NegativePoint(); /// Create invalid point 0,0
NegativePoint pa = new NegativePoint(0, 0); /// throws exception
NegativePoint pb = new NegativePoint(-5,-10); /// OK
44. #dotNETSpain2015
Structs & Value Types
- Normally used for performance issues (be sure you get it)
- Non-nullable (uninitialized struct has default values)
- No references (e.g. copy when passed as parameters)
- So two references can not point to the same struct (pointA =
pointB makes a copy), except ref params
- Sealed (no inheritance allowed)
- Default public constructor always exists (can not enforce valid
state)
- Always implement Equals to avoid reflection
45. #dotNETSpain2015
Reference Parameters
(ref, out)
void Increment(int number) { number++; }
void IncrementRef(ref int number) { number++; }
int a = 10;
int b = 10;
Increment(a);
IncrementRef(ref b);
Console.WriteLine("A: " + a); /// ???
Console.WriteLine("B: " + b); /// ???
47. #dotNETSpain2015
Reference Parameters
(ref, out)
void Inc1(Point p) { p.X++; }
void Inc2(Point p) {
p = new Point { X = p.X + 1, Y = p.Y };
}
void IncRef(ref Point p) {
p = new Point { X = p.X + 1, Y = p.Y };
}
Point pa = new Point {X = 1, Y = 1}; Inc1(pa);
Point pb = new Point { X = 1, Y = 1 }; Inc2(pb);
Point pc = new Point { X = 1, Y = 1 }; IncRef(ref pc);
/// pa.X, pb.X, pc.X ???
48. #dotNETSpain2015
Reference Parameters
(ref, out)
void Inc1(Point p) { p.X++; }
void Inc2(Point p) {
p = new Point { X = p.X + 1, Y = p.Y };
}
void IncRef(ref Point p) {
p = new Point { X = p.X + 1, Y = p.Y };
}
/// pa.X = 2
/// pb.X = 1
/// pc.X = 2 (pc is referencing a new object)
50. #dotNETSpain2015
Generic classes
public class MyList<T> {
public void Add(T elem)
{
...
}
...
}
MyList<int> listInts = new MyList<int>();
MyList<Car> listCars = new MyList<Car>();
MyList<Vehicle> listVehicles = new MyList<Vehicle>();
MyList<Vehicle> list = new MyList<Car> (); /// Are generics covariant
???
Vehicle[] arr = new Car[10]; /// Are arrays covariant ???
51. #dotNETSpain2015
Generic classes
Generics are invariant, arrays are covariant.
MyList<Vehicle> = new MyList<Car> (); /// Compile error
Vehicle[] arr = new Car[10]; /// OK, maybe runtime exceptions
string[] strarray = {"hello", "bye"};
object[] objarray = strarray;
objarray[0] = 123; /// ArrayTypeMismatchException
Vehicle[] vs = new Car[10];
vs[0] = new Motorbike(); /// ArrayTypeMismatchException
53. #dotNETSpain2015
Constraints on Generics
class MyClass<T> where T:
ClassOrInterface, // base class or interface
class, // any reference type
struct, // any value type
new() // with default public constructor
T elm = default(T); // null or 0-based value
54. #dotNETSpain2015
Variance in generic delegates
public delegate T Factory<T>();
void FillGarage(int numVehicles, Factory<Vehicle> factory) { /* ... */ }
Factory<Car> carFactory = () => new Car();
Factory<Vehicle> vehicleFactory = carFactory; // compile error
FillGarage(10, carFactory); // compile error
public delegate bool Predicate<T>(T elm);
void SendCarsToRepair(Car c, Predicate<Car> pred) {
if (pred(c)) {
...
Predicate<Vehicle> predFord = elm => elm.Make == "Ford";
SendCarsToRepair(myCar, predFord); // compile error
55. #dotNETSpain2015
Covariance (out) and
contravariance (in)
public delegate T Factory<out T>();
void FillGarage(int numVehicles, Factory<Vehicle> factory) { /* ... */ }
Factory<Car> carFactory = () => new Car();
Factory<Vehicle> vehicleFactory = carFactory; // NO compile error
FillGarage(10, carFactory); // NO compile error
public delegate bool Predicate<in T>(T elm);
void SendCarsToRepair(Car c, Predicate<Car> pred) {
if (pred(c)) {
...
Predicate<Vehicle> predFord = elm => elm.Make == "Ford";
SendCarsToRepair(myCar, predFord); // NO compile error
56. #dotNETSpain2015
Variance in interfaces
List<Car> carList = new List<Car>();
List<Vehicle> vehList = carList; // compile error
IEnumerable<Vehicle> enumVehicles = carList; // OK !!!
class MakeComparer : IComparer<Vehicle> {
public int Compare(Vehicle x, Vehicle y) { /* ... */
IComparer<Car> makeComp = new MakeComparer(); // OK !!!
Car[] cars = new Car[10];
Array.Sort(cars, makeComp); // OK
61. #dotNETSpain2015
int a = 5, b = 0;
int c = a / b; ???
int d = a % b; ???
int a = Integer.MaxValue - 2;
int b = a + 3; ???
int a = 1000000000;
int b = a * 2000000000; ???
62. #dotNETSpain2015
In integer arithmetics,
exceptions thrown in division by 0
int a = 5, b = 0;
int c = a / b; // DivideByZeroException
int d = a % b; // DivideByZeroException
int a = int.MaxValue - 2;
int b = a + 3; // b = -2147483646
int a = 1000000000;
int b = a * 2000000000; // b = 1321730048
63. #dotNETSpain2015
long l = 1000000000000L;
int i = (int) l; ???
double d = Math.Sqrt(-1); ???
double a = 5 , b = 0; ???
double c = a % b; ???
double d = a / b; ???
64. #dotNETSpain2015
No exceptions in floating -
point arithmetics
long l = 1000000000000L;
int i = (int) l; // i = -727379968
double d = Math.Sqrt(-1); // d = NaN
double a = 5 , b = 0;
double c = a % b; // c = NaN
double d = a / b; // d = Infinity
65. #dotNETSpain2015
Castings double to int
double nan = double.NaN;
double inf = double.PositiveInfinity;
double bignum = 1e20;
int inan = (int) nan; ???
int iinf = (int) inf; ???
int ibignum = (int) bignum; ???
68. #dotNETSpain2015
No floating-point when
high precision is required
double a = Double.MaxValue;
double b = a + 1;
// a = b = 1.79769313486232E308
double.Epsilon -> min. number different to 0
69. #dotNETSpain2015
Always use checked
integer arithmetic
(checked not available for floating-point
arithmetic)
checked {
...
}
System.ArithmeticException
System.DivideByZeroException
System.NotFiniteNumberException
System.OverflowException
71. #dotNETSpain2015
Watch out number overflows
Extra, Extra - Read All About It: Nearly All Binary
Searches and Mergesorts are Broken
1: public static int BinarySearch(int[] a, int key) {
2: int low = 0;
3: int high = a.length - 1;
4:
5: while (low <= high) {
6: int mid = (low + high) / 2;
7: int midVal = a[mid];
8:
9: if (midVal < key)
10: low = mid + 1
11: else if (midVal > key)
12: high = mid - 1;
13: else
14: return mid; // key found
15: }
16: return -(low + 1); // key not found.
17: }
74. #dotNETSpain2015
What's an immutable class ?
public class Point {
private readonly int x,y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public virtual int X { get { return x; } }
public virtual int Y { get { return y; } }
...
void setPoint(Point p) ...
75. #dotNETSpain2015
They must be sealed (or all setters)
public class MyPoint : Point {
private int x2,y2;
public override int X {
get { return x2; }
set { x2 = value; }
}
...
MyPoint p = new MyPoint(10,20);
miobj.setPoint(p);
p.X = 30; // VALUE CHANGED
76. #dotNETSpain2015
Watch out maps and sets
with mutable objects
var map = new Dictionary<Point,string>();
Point p = new Point(10,20);
map[p] = "my point";
Console.WriteLine(map[p]);
p.X = 11;
Console.WriteLine(map[p]);
77. #dotNETSpain2015
Watch out maps and sets
with mutable objects
var map = new Dictionary<Point,string>();
Point p = new Point(10,20);
map[p] = "my point";
Console.WriteLine(map[p]); // "my point"
p.X = 11;
Console.WriteLine(map[p]); // KeyNotFoundException
78. #dotNETSpain2015
Can we bypass invariants ?
class Point {
public X { get; set;}
public Y {get; set; }
...
class Line {
public Point StartPoint {
get { return startPoint; }
set {
if (value.X > 0 && value.Y > 0) {
startPoint = value;
}
...
79. #dotNETSpain2015
Never returns mutable objects
Always set a copy of mutable objects
Point p = new Point (1,1);
line.StartPoint = p;
p.X = -10;
Point p = line.StartPoint;
p.X = -10;
83. #dotNETSpain2015
Mutable and immutable classes
Prefer immutable classes.
For value objects try to build it at the
beginning and avoid set methods.
Make defensive copies where necessary
86. #dotNETSpain2015
Inheritance
public class InstrumentedHashSet<E> : HashSet<E> {
public int addCount;
public bool add(T item) {
addCount++;
return base.Add(item);
}
public static void Method(HashSet<string> a, string value) {
a.Add(value);
….
}
var set = new Instrumented<string>();
Method(set, "hello");
Method(set, "bye");
87. #dotNETSpain2015
Inheritance
Returns 0 !!!
public new bool add(T item)
Warning CS0108 'Instrumented<T>.Add(T)' hides inherited member
'HashSet<T>.Add(T)'. Use the new keyword if hiding was intended.
88. #dotNETSpain2015
Have to know intimately
the class you inherit from
public class InstrumentedHashSet<E> extends HashSet<E> {
private int addCount = 0;
@Override public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
...
InstrumentedHashSet<String> s = new InstrumentedHashSet<>();
s.addAll(Arrays.asList("How", "are", "you"));
System.out.println(s.addCount()); ???
93. #dotNETSpain2015
Sub sub = new Sub("hello"); // WHAT'S PRINTED ?
public class Super {
public Super() { Init(); }
virtual public void Init() { }
}
public class Sub : Super {
private String msg;
Sub(string msg) { this.msg = msg; }
override public void Init() {
msg = msg.ToUpper();
Console.WriteLine("MESSAGE: " + msg);
}
}
94. #dotNETSpain2015
Problem when calling
overridable methods in constructor
Constructors don't have to call overridable
methods
// NullReferenceException
The method is called from parent constructor
when the child part is not initialized yet.
96. #dotNETSpain2015
Equals and inheritance
protected bool Equals(Point other)
{ return X == other.X && Y == other.Y; }
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Point) obj);
}
Exactly the same type as "this"
97. #dotNETSpain2015
Liskov substitution
principle is not held
If S is a subtype of T, then objects of type T in a computer program can be
replaced by objects of type S (that is, objects of type S can be replacements
for objects of type T), without changing any important property of the
program.
public class ColorPoint : Point {
public string Color {get; set; }
...
var p1 = new Point {X=10, Y=20 };
var p2 = new Point {X=10, Y=20};
var pcolor = new ColorPoint {X=10, Y=20, Color="blue"};
p1.Equals(p2) // TRUE
p1.Equals(pcolor); // FALSE
98. #dotNETSpain2015
Equals and inheritance
Point:
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
var other = obj as Point;
return other != null && Equals(other);
}
ColorPoint:
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
var other = obj as ColorPoint;
return other != null && Equals(other);
}
protected bool Equals(ColorPoint other) {
return base.Equals(other) &&
string.Equals(Color, other.Color);
}
Equal or subtype of "Point"
99. #dotNETSpain2015
Symmetry rule is not held
var p = new Point {X=10, Y=20 };
var pcolor = new ColorPoint {X=10, Y=20,
Color="blue"};
p1.Equals(pcolor) // TRUE
pcolor.Equals(p1); // FALSE
100. #dotNETSpain2015
So we can make a special
check for parent classes
ColorPoint:
public override bool Equals(object obj)
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (!(obj is Point)) return false;
if (!(obj is ColorPoint)) return obj.Equals(this);
var other = obj as ColorPoint;
return other != null && Equals(other);
}
101. #dotNETSpain2015
Transitivity rule is not held
var pcolor1 = new ColorPoint {X=10, Y=20,
Color="blue"};
var pcolor2 = new ColorPoint {X=10, Y=20,
Color="red"};
var p = new Point {X=10, Y=20 };
pcolor1.Equals(p) // TRUE
p.Equals(pcolor2) // TRUE
pcolor1.Equals(pcolor2); // FALSE
102. #dotNETSpain2015
Quiz 1
uint[] foo = new uint[10];
object bar = foo;
Console.WriteLine("{0} {1} {2} {3}",
foo is uint[], // True
foo is int[], // ???
bar is uint[], // True
bar is int[]); // ???
103. #dotNETSpain2015
Quiz 1
uint[] foo = new uint[10];
object bar = foo;
Console.WriteLine("{0} {1} {2} {3}",
foo is uint[], // True
foo is int[], // False
bar is uint[], // True
bar is int[]); // True
104. #dotNETSpain2015
Quiz 1
The CLI has the concept of "assignment compatibility"... The assignment from source value to
target variable has to be "representation preserving".
One of the rules of the CLI is "if X is assignment compatible with Y then X[] is assignment
compatible with Y[]".
That is not a rule of C#. C#'s array covariance rule is "if X is a reference type implicitly
convertible to reference type Y (via a reference or identity conversion) then X[] is implicitly
convertible to Y[]". That is a subtly different rule!
http://blogs.msdn.com/b/ericlippert/archive/2009/09/24/why-is-covariance-of-value-typed-
arrays-inconsistent.aspx
105. #dotNETSpain2015
Quiz 2
short sss = 123;
object ooo = sss; // Boxing
// OK, compile error or runtime error ???
int iii = (int)sss;
int jjj = (int)(short)ooo;
int kkk = (int)ooo;
106. #dotNETSpain2015
Quiz 2
Why? Because a boxed T can only be unboxed to T
short sss = 123;
object ooo = sss; // Boxing
int iii = (int)sss; // OK
int jjj = (int)(short)ooo; // OK
int kkk = (int)ooo; // Runtime error
http://blogs.msdn.com/b/ericlippert/archive/2009/03/19/representation-and-identity.aspx