Effective C#

107
#dotNETSpain2015 Leo Antoli @lantoli Effective C# .NET Conference 2015 Y A X B

Transcript of Effective C#

Page 1: Effective C#

#dotNETSpain2015

Leo Antoli @lantoliEffective C#

.NET Conference 2015

Y

AX B

Page 2: Effective C#

#dotNETSpain2015

Page 3: Effective C#

#dotNETSpain2015

WARNING:Most of the code in these slides is about how NOT to do it

Page 4: Effective C#

#dotNETSpain2015

In order to master a language...

Wife mine this night

building an unusual

dinner at my home,

invited you are.

Page 5: Effective C#

#dotNETSpain2015

Let's get started...

Page 6: Effective C#

#dotNETSpain2015

string and String

Any difference ?

string a = "hello";

String b = "Hello";

Page 7: Effective C#

#dotNETSpain2015

string and String

NO DIFFERENCE but prefer string

string -> C# alias for System.String

String -> System.String is a CLR type

Page 8: Effective C#

#dotNETSpain2015

StringBuilder

string[] listStr = { "how ", "are", "you"};string res = "";foreach (string str in listStr) {

res += str;}

Page 9: Effective C#

#dotNETSpain2015

StringBuilder

string str = "a" + "b" + "c" + "d" + "e" + "f";

string str = new StringBuilder().

Append("a").Append("b").Append("c").

Append("d").Append("e").Append("f").ToString()

Page 10: Effective C#

#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()

Page 11: Effective C#

#dotNETSpain2015

Strong-typed Language

Any difference ?

int v = 10;string res = "hello";foreach (string str in listStr) { ... }

var v = 10;var res = "hello";foreach (var str in listStr) { ... }

Page 12: Effective C#

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

Page 13: Effective C#

#dotNETSpain2015

Overloading and Overriding

Polymorphism types:Overloading (params) - compilation time - decide method to call on input paramsOverriding (inheritance) - execution - decide method to call on instance class

Parent called

Child called

Parent called

Page 14: Effective C#

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

Page 15: Effective C#

#dotNETSpain2015

Overloading and Overriding

true

false

public bool Equals(Point p)

public override bool Equals(object obj)

Page 16: Effective C#

#dotNETSpain2015

String equality

string s1 = "hello";

string s2 = "HELLO".ToLower();

object o1 = s1;

object o2 = s2;

public void eq(bool expr) { Console.WriteLine(expr) };

eq( s1 == s2); // ???

eq( o1 == o2 ); // ???

eq( s1 == o2 ); // ???

eq( s1.Equals(s2) ); // ???

eq( s1.Equals(o2) ); // ???

eq( o1.Equals(s2) ); // ???

Page 17: Effective C#

#dotNETSpain2015

String equality

string s1 = "hello";

string s2 = "HELLO".ToLower();

object o1 = s1;

object o2 = s2;

public void eq(bool expr) { Console.WriteLine(expr) };

eq( s1 == s2 ); // True

eq( o1 == o2 ); // False

eq( s1 == o2 ); // False

eq( s1.Equals(s2) ); // True

eq( s1.Equals(o2) ); // True

eq( o1.Equals(s2) ); // True

Page 18: Effective C#

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

Page 19: Effective C#

#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(); // ???

Page 20: Effective C#

#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

Page 21: Effective C#

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

...

Page 22: Effective C#

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

Page 23: Effective C#

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

};

Page 24: Effective C#

#dotNETSpain2015

CLR & C#

+ ++ +C

Page 25: Effective C#

#dotNETSpain2015

CLR & C# (assembly compatibility)

Page 26: Effective C#

#dotNETSpain2015

CLR & C# (properties vs fields)

FIELD:

public String Description;

PROPERTY:

private String comment;

public String Comment

{

get { return comment; }

set { comment = value; }

}

Page 27: Effective C#

#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

Page 28: Effective C#

#dotNETSpain2015

CLR & C# (readonly vs const)

public const int DefaultPageSize = 10;

public static int DefaultPageSize2 { get { return 10; } }

public static readonly int DefaultPageSize3 = 10;

Page 29: Effective C#

#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

Page 30: Effective C#

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

Page 31: Effective C#

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

Page 32: Effective C#

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

Page 33: Effective C#

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

Page 34: Effective C#

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

Page 35: Effective C#

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

Page 36: Effective C#

#dotNETSpain2015

Value and Reference Types

Page 37: Effective C#

#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

Page 38: Effective C#

#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); // ???

Console.WriteLine("qa: " + qa); // ???

Console.WriteLine("qb: " + qb); // ???

Page 39: Effective C#

#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

Page 40: Effective C#

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

}

Page 41: Effective C#

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

}

Page 42: Effective C#

#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

?

Page 43: Effective C#

#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

Page 44: Effective C#

#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

Page 45: Effective C#

#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); /// ???

Page 46: Effective C#

#dotNETSpain2015

Reference Parameters (ref, out)

Ref is as an alias or reference, it's an in/out param

Console.WriteLine("A: " + a); /// 10

Console.WriteLine("B: " + b); /// 11

Page 47: Effective C#

#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 ???

Page 48: Effective C#

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

Page 49: Effective C#

#dotNETSpain2015

Generics

Page 50: Effective C#

#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 ???

Page 51: Effective C#

#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

Page 52: Effective C#

#dotNETSpain2015

Constraints on Generics

Assignments ?

T elm = null;

T elm = new T();

Common ancestor ?

T elm;

elm.X = 10;

Page 53: Effective C#

#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

Page 54: Effective C#

#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

Page 55: Effective C#

#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

Page 56: Effective C#

#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

Page 58: Effective C#

#dotNETSpain2015

Numbers

Page 59: Effective C#

#dotNETSpain2015

Special numbers

int a = ???a == -a

int b = ???b == -b

double d = ???d != d

Page 60: Effective C#

#dotNETSpain2015

Special numbers

int a = 0a == -a

int b = int.MinValueb == -b

double d = double.NaNd != d But d.Equals(d) !!!

Page 61: Effective C#

#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; ???

Page 62: Effective C#

#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

Page 63: Effective C#

#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; ???

Page 64: Effective C#

#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

Page 65: Effective C#

#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; ???

Page 66: Effective C#

#dotNETSpain2015

Castings double to int

int inan // int.MinValue

int iinf // int.MinValue

int ibignum // int.MinValue

Page 67: Effective C#

#dotNETSpain2015

Floating-point precision

double a = Double.MaxValue

double b = a + 1; ???

Page 68: Effective C#

#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

Page 69: Effective C#

#dotNETSpain2015

Always use checked integer arithmetic

(checked not available for floating-point arithmetic)

checked {...}

System.ArithmeticException

System.DivideByZeroException

System.NotFiniteNumberException

System.OverflowException

Page 70: Effective C#

#dotNETSpain2015

High precision floating-point

decimal (28 significant bits)

NO BigDecimal

BigInteger

Page 71: Effective C#

#dotNETSpain2015

Watch out number overflows

Extra, Extra - Read All About It: Nearly All Binary

Searches and Mergesorts are Broken1: 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: }

Page 72: Effective C#

#dotNETSpain2015

Right solution

int mid = low + ((high - low) / 2);

unsigned mid = (low + high) >> 1;

Page 73: Effective C#

#dotNETSpain2015

Mutability

Page 74: Effective C#

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

Page 75: Effective C#

#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

Page 76: Effective C#

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

Page 77: Effective C#

#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

Page 78: Effective C#

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

}

...

Page 79: Effective C#

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

Page 80: Effective C#

#dotNETSpain2015

public class Student {

public String Name { get; private set; }

public List<Course> Courses { get; private set; }

...

Page 81: Effective C#

#dotNETSpain2015

Lists are mutable

And non-empty arrays are mutable too

List<Course> list = student.Courses;

list.Add(myNewCourse);list.Clear();

Page 82: Effective C#

#dotNETSpain2015

To create immutable lists

Enumerable.Empty<T>

list.AsReadOnly

ReadOnlyCollection

Page 83: Effective C#

#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

Page 84: Effective C#

#dotNETSpain2015

Inheritance

Page 85: Effective C#

#dotNETSpain2015

Inheritance

In general prefer composition over

inheritance.

Inheritance breaks encapsulation.

Page 86: Effective C#

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

Page 87: Effective C#

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

Page 88: Effective C#

#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()); ???

Page 89: Effective C#

#dotNETSpain2015

Problem when using overridable methods internally

Returns 6 !!!

We need to know what public/overridable methods do internally.

Page 90: Effective C#

#dotNETSpain2015

Better to delegate

public class InstrumentedHashSet<E> extends Set<E> {

private int addCount = 0;

private Set<E> set = new HashSet<E>();

@Override public boolean add(E e) {

addCount++;

return set.add(e);

}

@Override public boolean addAll(Collection<? extends E> c) {

addCount += c.size();

return set.addAll(c);

}

…….

Page 91: Effective C#

#dotNETSpain2015

Constructors and inheritance

public class Child : Parent {private int value;public Child(int value) {

this.value = value;}}

// What's the difference ???public Child(int value) : base() {

this.value = value;}

Page 92: Effective C#

#dotNETSpain2015

Constructors and inheritance

public class Parent {private int x;public Parent(int x) {

this.x = x;...

public Child(string value) {this.value = value; // Is super called ???

}

Page 93: Effective C#

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

}

}

Page 94: Effective C#

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

Page 95: Effective C#

#dotNETSpain2015

Equals and inheritance

Resharper / Generate equality members

public class Point

{

public int X {get; set; }

public int Y {get; set; }

}

Page 96: Effective C#

#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"

Page 97: Effective C#

#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

Page 98: Effective C#

#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"

Page 99: Effective C#

#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

Page 100: Effective C#

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

}

Page 101: Effective C#

#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

Page 102: Effective C#

#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[]); // ???

Page 103: Effective C#

#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

Page 104: Effective C#

#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

Page 105: Effective C#

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

Page 106: Effective C#

#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 errorhttp://blogs.msdn.com/b/ericlippert/archive/2009/03/19/representation-and-identity.aspx

Page 107: Effective C#

#dotNETSpain2015

Leo Antoli @lantoliEffective C#

¡¡¡Si te ha gustado no olvides rellenar la encuesta!!!Thanks

Y

AX B