Tuesday, June 30, 2015

C# Nutshell Chapter 3 Creating Types in C#

Object Initializers

To simplify object initialization, any accessible fields or properties of an object can
be set via an object initializer directly after construction.

public class Bunny
public string Name;
public bool LikesCarrots;
public bool LikesHumans;
public Bunny () {}
public Bunny (string n) { Name = n; }

// Note parameterless constructors can omit empty parentheses
Bunny b1 = new Bunny { Name="Bo", LikesCarrots=true, LikesHumans=false };
Bunny b2 = new Bunny ("Bo") { LikesCarrots=true, LikesHumans=false };


Properties look like fields from the outside, but internally they contain logic, like
methods do.
A property is declared like a field, but with a get/set block added. Here’s how to
implement CurrentPrice as a property:
public class Stock
decimal currentPrice; // The private "backing" field
public decimal CurrentPrice // The public property
get { return currentPrice; } set { currentPrice = value; }

Const v.s. static readonly

A static readonly field is also advantageous when exposing to
other assemblies a value that might change in a later version.
For instance, suppose assembly X exposes a constant as follows:
public const decimal ProgramVersion = 2.3;
If assembly Y references X and uses this constant, the value 2.3
will be baked into assembly Y when compiled. This means that
if X is later recompiled with the constant set to 2.4, Y will still
use the old value of 2.3 until Y is recompiled. A static
readonly field avoids this problem.
Another way of looking at this is that any value that might
change in the future is not constant by definition, and so should
not be represented as one.


A struct is similar to a class, with the following key differences:
  • • A struct is a value type, whereas a class is a reference type.
  • • A struct does not support inheritance (other than implicitly deriving from object, or more precisely, System.ValueType).

A struct can have all the members a class can, except the following:
  • • A parameterless constructor
  • • A finalizer
  • • Virtual members
public struct Point
int x = 1; // Illegal: cannot initialize field
int y;
public Point() {} // Illegal: cannot have
// parameterless constructor
public Point (int x) {this.x = x;} // Illegal: must assign field y

Class Access Modifiers

Fully accessible. This is the implicit accessibility for members of an enum or
Accessible only within containing assembly or friend assemblies. This is the
default accessibility for non-nested types.
Accessible only within containing type. This is the default accessibility for
members of a class or struct.
Accessible only within containing type or subclasses.

Class2 is accessible from outside its assembly; Class1 is not:

class Class1 {} // Class1 is internal (default)
public class Class2 {}

ClassB exposes field x to other types in the same assembly; ClassA does not:

class ClassA { int x; } // x is private (default)
class ClassB { internal int x; }

Functions within Subclass can call Bar but not Foo:

class BaseClass
void Foo() {} // Foo is private (default)
protected void Bar() {}
class Subclass : BaseClass
void Test1() { Foo(); } // Error - cannot access Foo
void Test2() { Bar(); } // OK

Constructors and Inheritance

A subclass must declare its own constructors. The base class’s constructors are
accessible to the derived class, but are never automatically inherited.

public class Baseclass
public int X;
public Baseclass () { }
public Baseclass (int x) { this.X = x; }
public class Subclass : Baseclass { }
//the following is illegal:
Subclass s = new Subclass (123);

Subclass must hence “redefine” any constructors it wants to expose. In doing so,
however, it can call any of the base class’s constructors with the base keyword:
public class Subclass : Baseclass
public Subclass (int x) : base (x) { }

If a constructor in a subclass omits the base keyword, the base type’s parameterless constructor is implicitly called. If the base class has no accessible parameterless constructor, subclasses are forced to use the base keyword in their constructors.

public class BaseClass
public int X;
public BaseClass() { X = 1; }  //without this, subclass has to use 'base' keyword
public class Subclass : BaseClass
public Subclass() { Console.WriteLine (X); } // 1

Using Interface or Superclass

As a guideline:
• Use classes and subclasses for types that naturally share an implementation.
• Use interfaces for types that have independent implementations.
Consider the following classes:
abstract class Animal {}
abstract class Bird : Animal {}
abstract class Insect : Animal {}
abstract class FlyingCreature : Animal {}
abstract class Carnivore : Animal {}
// Concrete classes:
class Ostrich : Bird {}
class Eagle : Bird, FlyingCreature, Carnivore {} // Illegal
class Bee : Insect, FlyingCreature {} // Illegal
class Flea : Insect, Carnivore {} // Illegal

The Eagle, Bee, and Flea classes do not compile because inheriting from multiple
classes is prohibited. To resolve this, we must convert some of the types to interfaces.
The question then arises, which types? Following our general rule, we could
say that
insects share an implementation, and birds share an implementation, so
they remain classes. In contrast, flying creatures have independent mechanisms
for flying, and carnivores have independent strategies for eating animals, 
so we
would convert FlyingCreature and Carnivore to interfaces:
interface IFlyingCreature {}
interface ICarnivore {}

Enum initialization

It is different from other type initialization. 

public enum BorderSide { Left, Right, Top, Bottom }
BorderSide topSide = BorderSide.Top;
bool isTop = (topSide == BorderSide.Top); // true

Enum Operator Type-safety Issues

Since an enum can be cast to and from its underlying integral type, the actual value
it may have may fall outside the bounds of a legal enum member

BorderSide b = BorderSide.Bottom;
b++; // No errors

An invalid BorderSide would break the following code:

void Draw (BorderSide side)
if (side == BorderSide.Left) {...}
else if (side == BorderSide.Right) {...}
else if (side == BorderSide.Top) {...}
else {...} // Assume BorderSide.Bottom

One solution is to add another else clause:
else if (side == BorderSide.Bottom) ...
else throw new ArgumentException ("Invalid BorderSide: " + side, "side");

Another workaround is to explicitly check an enum value for validity. The static
Enum.IsDefined method does this job:

BorderSide side = (BorderSide) 12345;
Console.WriteLine (Enum.IsDefined (typeof (BorderSide), side)); // False

Monday, June 29, 2015

C# Nutshell Chapter 1-2 Basics

"==" makes a compile-time decision

int x = 1;
int y = 1;
x == y // true

object x = 1;
object y = 1;
x == y // false; referential equality from object's == operator

"Equals" is resolved at runtime, according to the object's actual type

object x = 1;
object y = 1;
x.Equals(y);  //true

object class static helper method providing null-safe equality comparision

object x = 3, y = 3;
Console.WriteLine (object.Equals (x, y)); // True
x = null;
Console.WriteLine (object.Equals (x, y)); // False
y = null;
Console.WriteLine (object.Equals (x, y)); // True

implicitly assigned default value

static void Main()
int x;
Console.WriteLine (x); // Compile-time error

class Test
static int x;
static void Main() { Console.WriteLine (x); } // 0

"ref" and "out" keywords

  • "ref" - pass in as reference rather than value
  • "out" - no need to be assigned before going into the function, but must be assigned before comes out of the function. 
  • both "ef"and "out" are passed in by reference

"params" modifier

The params parameter modifier may be specified on the last parameter of a method
so that the method accepts any number of parameters of a particular type.

static int Sum (int a, int b, params int[] ints)
int sum = 0;
for (int i = 0; i < ints.Length; i++)
sum += ints[i]; // Increase sum by ints[i]
return sum;

Optional parameters

void Foo (int x = 23) { Console.WriteLine (x); }
Foo(); // 23

Mandatory parameters must occur before optional parameters in both the method declaration and the method call public method that’s called from another assembly requires recompilation of both assemblies.

void Foo (int x = 0, int y = 0) { Console.WriteLine (x + ", " + y); }
void Test()
Foo(1); // 1, 0

Named arguments

Rather than identifying an argument by position, you can identify an argument by

void Foo (int x, int y) { Console.WriteLine (x + ", " + y); }
void Test()
Foo (y:2, x:1); // 1, 2

Named arguments are particularly useful in conjunction with optional parameters.
For instance, consider the following method:
void Bar (int a = 0, int b = 0, int c = 0, int d = 0) { ... }
We can call this supplying only a value for d as follows:
Bar (d:3);