Brief Notes on Essential C# 3.0: For .NET Framework 3.5 (2nd Edition)

I'll be updating this as I read, but the date/time of the post will remain the same. This will by no means make up for a reading of the book - Essential C# 3.0: For .NET Framework 3.5 (2nd Edition). (Link to my review will be posted once I finish the book.)

I work primarily on the Web, so that will be my main focus from reading this book. Some chapters may not be present here, depending upon what I think is note-worthy at the time.

Chapter 1

Immutable - strings are immutable in that when you reassign the variable, it points to a new location in memory.

XML delimited comments are /** comment **/ and /// comment (wasn't aware of the first one).

Chapter 21

Common Language Infrastructure (CLI) is an international standard containing the specifications for the Common Intermediate Language (CIL) and the runtime.

  1. C# compiler generates to Common Intermediate Language (CIL). (This is what other supported languages, like VB, J#, etcetera, are converted to as well.)
  2. Generally at execution time (although you can do this earlier, on the machine that will actually run the code - NGEN) code is compiled from CIL to machine code. This is done by the just-in-time (JIT) compiler.
  3. Now the C# code execution is managed by the Virtual Execution System (VES) / runtime. "The runtime is responsible for loading and running programs and providing additional services (security, garbage collection, and so on) to the program as it executes."
    • Managed code: "the code that executes under the context of an agent such as the runtime" Allows for additional services to be injected into a program
    • Managed execution: "the process of executing under control of the runtime"
    • Managed data: control over the allocation and deallocation of memory for the data
  4. Common Language Runtime (CLR) is "the Microsoft-specific implementation of the runtime for the .NET platform."

Garbage collection

Built-in to the runtime. Does not happen at pre-determined or set times. Less likely to run "if memory on the computer is still largely untapped."

In .NET, "objects that have lived for only a short period will be cleaned up sooner than objects that have already survived garbage collection sweeps."

Objects that remain are compacted together, which "often results in faster instantiation of new objects."

Chapter 2 

static methods must have a prefix of the type. text = string.Format();

instance methods instead prefix with the variable or instance name. firstName.ToUpper();

String immutability: text.ToUpper(); creates a new string that would need to be saved or used directly.

System.Text.StringBuilder, however, modifies the data in the variable, and does not return a new string.

Reference types: "contains a pointer, an address, or a reference to a location in memory that is different from where the actual data resides." (For example, strings. Covered in Chapter 5.) Null can only be assigned to a reference type, unless you use the nullable modifier. For example, int? count = null;. 

string faxNumber; != string faxNumber = null; - in the latter case, the variable has been set.

void: Don't expect any data. There is no data type.

var was added in C# 3.0 to support anonymous types - types declared dynamically. Implicitly defined by what the compiler determines. Allows for "joining (associating) data types or reducing the size of a particular type down to fewer data elements." (See pg 54-5 for example.)

Categories of types

  • Value types:
    • memory: data is stored on the stack
    • store the data
    • each variable gets its own place in memory
  • Reference types:
    • memory: data is stored on the heap
    • store the reference to where the data is stored, not the actual value

Stack and heap: See Figure 2.2 on page 57. 


(type) for explicit casting. Parse() for string to numeric. System.Convert for number types to other base types. TryParse() as of C# 2.0 (returns false instead of throwing an exception).

All types support ToString(): if the type has an explicit implementation the string representation of the data is returned, otherwise the name of the data type is.


Page 65-80. 

"Arrays provide a means of declaring a collection of data items that are of the same type using a single variable."

string[] languages;

Use commas for additional dimensions: string[,] cells;

Assigning after declaring requires new. (new: "tells the runtime to allocate memory for the data type")

Jagged array: array of arrays. Page 70. Length returns number of elements in the outside/first array.

Arrays have a fixed length, so can't be changed without re-creating the array. For multi-dimensional, it's the number of elements total.

Array is reference type - use Clone() method for a new copy.

Common errors 79-80.

Chapter 3 

Constants (const) are evaluated and determined at compile time. 

for loops can consist of multiple subexpressions (see page 124), although generally you shouldn't.

In a foreach loop, the identifier (foreach(type identifier in collection)) is read-only.

C# preprocessor directives listed on page 136.

Chapter 4 

To the compiler, the namespaces System.Collections and System.Collections.Generics are at the same level. Hierarchy implications are for human-readability only. In fact, namespaces are nested, which basically means that in using statements, you still have to use System.Text, even if you're using System.

'All types are compatible with the data type object.'

Over time, look for ways to simplify your code by refactoring - moving blocks of code into methods.

Alias using by way of using namespaceAlias = namespace. For example, using CountDownTimer = System.Timers.Timer;

Command-line arguments covered on 165-6.

When passing parameters, whether they be reference or value types, the original variables are not changed (value is passed, not the reference). However, if you add ref before the parameters (for both method and call), they can be. See 169.

See 170-1 for output parameters (out).

Parameter arrays covered on 172-4.

Common exception types listed on 187-8.

Avoid throwing exceptions for known errors or conditions.

Chapter 5 

OOP on 196-9, 202, 214.

An object is an instance of a class, which is a template for an object. 

Instance fields (no static modifier) can only be accessed from an instance of the class (an object).

this is an implicit field within every class that returns an instance of the object itself. See 206-7.

public access modifier means field is available outside of class, while private is the opposite (and the default).

Use properties to modify private fields.

public type Property {
   get { return _;}
   set { _ = value;}

It's a good idea to use properties within the class as well, since validation can take place in one area only.

Remove get and/or set for write- and/or read-only.

Access modifier (public, private, …) can be applied to get or set, but only on one.

Properties can be virtual fields - for example, Employee.Name could be FirstName + " " + LastName.

Properties and methods cannot be ref or out parameter values. Copy to a variable first, pass that, and pass variable back to property.

Constructors are methods with no return types, with a name identical to class name, that allow fields to be initialized.

Default constructor, taking no parameters, is created by the C# compiler at compilation, unless a costructor is declared. If you want a default one - Employee() for example, after you've added Employee(FirstName, LastName) - you'll have to add it yourself.

Object initializers on 232.

Overloading constructors involves different number or types of parameters. Use constructor initializers to eliminate code duplication by calling other constructors, using this. See 235.

Even better, possibly use a private method to Initialize() properties/object? See 236-7.

Anonymous types covered on 237-9.

static fields are shared across multiple instances of an object, and belong to the class (not the instance). See 240-241 for examples.

"In designing objects, programmers should take care to declare both fields and methods appropriately as static or instance-based. In general, you should declare […] methods that access instance data (where the instance is not passed in as a parameter) as instance methods. Static fields store data corresponding to the class […] Instance fields store data associated with the object." (243)

static constructors are called by the runtime upon first access to the class. See 245.

Better to have a static property than a public static field (246).

static classes can't be instantiated, but usually don't need to be (objects would be pointless). See 247-8, which involves math. These classes also can't be extended (they're marked as sealed).

It's possible to add instance methods to any class using extension methods. However, use these sparingly, since there's typically a better way. See 249-50.

const fields are automatically static.

readonly fields declare "that the field value is modifiable only from inside the constructor or directly during declaration," so they can vary and change instance to instance. See 251-2.

Nested classes are classes defined within another class. Allow for the private modifier, and are rare. See 253-4.

Partial classes allow one class to be defined in multiple files (such as a nested class in another file).

Partial methods are also allowed, as long as they return void (partial void Class). See 256-9.

Chapter 6 

When defining a derived class - methods defined on the base class are avilable to all subclasses - use a colon followed by the base class. This inheritence follows all the way down, to an unlimited numder of subclasses from subclasses from base classes.

Derived classes can be cast to the base class, implicitly. However, the reverse is not true.

Private members are not available to derived classes, while protected members are only available to derived classes.

While you can only derive from one base class in C#, you can work around this, if necessary, by using aggregation, by calling an instance of the second class you wish to derive from.

sealed classes cannot be derived from. You can assign these on base as well as derived classes (so that no further derivations are possible).

By using the virtual modifier in the base class and override in the derived classes, instance methods and properties can be overriden. Fields and static members cannot be overriden. Override implies virtual (unless you've used override sealed).

Be careful when allowing methods to be overriden, as the most derived implementation will be used, so keep core functionality non-virtual. See 276-7 for one such way.

Use new to break out of the above. See 280-1 for something of an example.

Use base.Method() to use the base classes' method. Useful for when derived class 'extends' the method in some way. See 282-3 for a good example (ToString()).

If the base class has no default constructor (ClassName()), this must be defined in the derived class constructor. See 284.

abstract classes are available for derivation only. They typically define what derived classes should contain, methods or properties, but not how to implement them. Abstract implies virtual.

See 286-7, 289-90 for an example.

Since everything derives from System.Object, there are methods that are available on everything. See 290-1 for these (ToString() is one, which allows for 33.ToString()).

Use is to check what the underlying type is. See 292. Use as to attempt to convert to a type, and assign null if the conversion fails.

Chapter 7

Interfaces allow common method signatures across classes, with different functionality, and without inheritence. You can therefore switch implementations without changing the calling code. See 297.

Method declarations are followed by a semi-colon, not curly braces, and properties must be used instead of fields. The interface has no implementation, no actual code nor fields. All members are public, and cannot be declared as such or otherwise (no access modifiers).

Classes can implement multiple interfaces, and are listed after the class name, along with the base class (if any).

If a class uses an interface, all members of the interface must be implemented. Throw NotImplementedException as needed.

Choosing between an explicit and implicit interface implementation covered on 306-7. If in doubt, use explicit.

Multiple interface inheritance example on page 310.

Changing an interface can be intensive, as all classes that implement the interface must also change. See 316-7 for an example of a version-safe way.

See 318 for a comparison of abstract classes with interfaces.

Chapter 8

Reminder: types are either reference types or value types.

Value types directly contain their value, and point to a location in memory, so each new value type, or using a value type within a method, means more memory usage. These derive from System.ValueType.

Reference types store the reference / memory address of the value, not the value itself. This means that two variables can point to the same reference, and therefore changing one would change the other.

string and object are reference types, the rest of the primitive types are value types.

Use struct to create a custom value type. Example on 324-5. See initialization rules on 326.

Boxing/unboxing content on 329-35, which seems to be out of place here (went over the head).

Use enum to create a set of values, with each value having a name. See 335-40. Use singular when declaring.

To combine enums, use a plural name. See 340-4 for additional details on doing this.


Reading log

(If you're interested, otherwise, since I'm reading the electronic version of the book, I need someplace to keep track of this.)

  • 08/15/2008: > xlvi
  • 08/16/2008: 1 > 30, 749 > 770, 31 > 82, 83 > 146
  • 08/21/2008: 147 > 194
  • 08/23/2008: 195 > 260
  • 08/24/2008: 261 > 294 (re-read at some point)
  • 08/29/2008: 295 > 320 (need to re-read), 320 > 346 (also need to re-read)