A productive-focused language like C# has so much syntax sugar that I feel not so comfortable with, because I used to be a C++ programmer and everything in the C++ kingdom appears to be straight forward, although, at the same time, tend to be fallible.
The most serious problem I find about Microsoft’s tools is that up there exist so many auto-generated codes that I cannot know them all in details. I mean, that is good, you can ship as much stuff as possible within limited time. The problem is, it is not good for curious people like me, nor for the beginner. Back in school, when we build something, we build it from scratch. Perhaps, reinvent the wheel is the best way to learn the wheel. IEDs such as Visual Studio and Eclipse make people so lazy and forget to remember and to think.
Consequently, in my spare time, I would do write every code with VIM.
The following is my notes from Chapter 9 through Chapter 13.
9 Well-Formed Types 357
Overriding Object Members
Guidelines for overriding System.Object members. Reference on request.
GetHashCode()
ReferenceEquals() Object Identity vs. Equals() Equal Object Values
Calling ReferenceEquals() on value types will always return false since
Operator Overloading
public static
avoid recursive loop (leftHandSide == null) when check equality
One of the parameters of a operator must be the containing type
Generate an XML doc file csc /doc:Comments.xml DataStorage.cs
tools to generate docs: GhostDoc, NDoc
GC
Weak reference save the reference for future reuse (memory cache) private WeakReference Data;
Finalizer: ~ClassName() (注意,不是Dispose) like Java’s finalize()一般~ClassName()会检查Dispose是否已经被调用
Deterministic finalization with the using statement
The IDisposable interface defines the details of the pattern with a single method called Dispose(), which developers call on a resource class to “dispose” of the consumed
HOWEVER, there is a chance that an exception will occur before the dispose call resources. If this happens, Dispose() will not be invoked and the resource cleanup will have to rely on the finalizer.
SO 2 ways:
try / finally
using statement and all variables are of the same type and they implement IDisposable
The f-reachable queue is a list of all the objects that are ready for
garbage collection and that also have finalization implementations. System.GC.SuppressFinalize(reference) can remove reference instance from f-reachable queue.
Resource Utilization and Finalization Guidelines. Refer the book page 400.
Generally, ~Destructor() calls Dispose()
Lazy Initialization: Defer the init of an object until it is required.
- `System.Lazy<T>`
123456789101112131415161718192021222324252627
// Lazy InitializationusingSystem.IO;classDataCache{// ...publicDataCache(stringFileStreamName){_FileStream=newLazy<TemporaryFileStream>(()=>newTemporaryFileStream(FileStreamName));// the lambda expression provides a means of passing the// instructions for what will happen, but not actually performing those// instructions until explicitly requested.}publicTemporaryFileStreamFileStream{get{return_FileStream.Value;}}privateLazy<TemporaryFileStream>_FileStream;// ...}
10 Exception Handling 405
Multiple Exception Types
Catching Exception
General Catch Block
Guidelines
Custom Exceptions
11 Generics 421
Generics
name rule as EntityCollection<TEntity>
public class Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>: ... {...}, TRest can be used to store another tuple.
Tuple.Create("555-55-5555", new Contact("Tim Pan")); == new Tuple<string, Contact>("555-55-5555", new Contact("Tim Pan"));
Constraints: To avoid a runtime exception and instead provide a compile-time error, C# enables you to supply an optional list of constraints for each type parameter declared in the generic class by using where.
Interface Constraints
Base Class Constraints
struct/class Constraints
Multiple Constraints
Constructor Constraints: ensure default ctor like new ctor()
Generic Methods
explicit cast is preferred at most of the times.
Variance Type1<Type2> and Type3<Type2> are not covariant.
Covariance TODO: Read Again
with out for getters
with in for setters
Contra variance
Generic Internals
Language Contrast: Sun’s implementation of generics for Java occurs within the compiler entirely, not within the JVM. JVM cannot support generics for value types.
// Needing the type parameter to support an interface or exception thrownpublicclassBinaryTree<T>{// ...publicPair<BinaryTree<T>>SubItems{get{return_SubItems;}set{IComparable<T>first;first=(IComparable<T>)value.First.Item;if(first.CompareTo(value.Second.Item)<0){// first is less than second.}else{// second is less than first.}_SubItems=value;}}privatePair<BinaryTree<T>>_SubItems;}// Declaring the interface constrantpublicclassBinaryTree<T>whereT:System.IComparable<T>{// ...publicPair<BinaryTree<T>>SubItems{get{return_SubItems;}set{IComparable<T>first;// Notice that the cast can now be eliminated.first=value.First.Item;if(first.CompareTo(value.Second.Item)<0){// first is less than second}else{// second is less than or equal to first}_SubItems=value;}}privatePair<BinaryTree<T>>_SubItems;}
123456789
// multiple constraints// an AND relationship is assumedpublicclassEntityDictionary<TKey,TValue>:Dictionary<TKey,TValue>whereTKey:IComparable<TKey>,IFormattablewhereTValue:EntityBase{// ...}
1234567891011
// constructor constraints ensure default ctors only// Ctors with parameters are NOT supportedpublicclassEntityDictionary<TKey,TValue>:Dictionary<TKey,TValue>whereTKey:IComparable<TKey>,IFormattablewhereTValue:EntityBase<TKey>,new(){//...TValuenewEntity=newTValue();//...}
12 Delegates and Lambda Expressions 469
Introducing Delegates
Why Delegates
C/C++ method pointer == delegate, which encapsulates methods as objects, enabling an indirect method call bound at runtime.
Delegate As Data Types
Although all delegate data types derive indirectly from System.Delegate, the C# compiler does not allow you to define a class that derives directly or indirectly (via System.MulticastDelegate) from System.Delegate.
Delegate Internals
TODO
Instantiating Delegates
Anonymous Methods
BubbleSort(items, delegate(int first, int second){return first<second})
can be parameterless: Parameterless Anonymous Methods
System-Defined Delegates: Func<>
Func<> can also be used for generic delegate.
Func<int, int, bool> the last type is the return type.
System.Action should be used for delegates that have no return.
However, the delegate’s name provides a more explicit indication of what it is for, whereas Func<> provides nothing more than an understanding of the method signature.
Generic Func delegate and explicitely defined delegate are not compatible. e.g. Func<int, int, bool> and ComparisonHandler are not compatible. But some degree of casting is allowed.
BubbleSort(items, (int first, int second) => {return first < second;})
go/goes to, becomes, such that: “integers first and secondgo to returning the result of first less than second”
BubbleSort(items, (first, second) => {return first < second;})
statement lambdas can omit parameter types as long as the compiler can infer the types
typically a statement lambda uses only two or three statements in its statement block.
Expression Lambdas
no statement block, only an expression
names.Where(name => name.Contains(" ")), “names where names dot contains a space”
you cannot use the typeof() operator (see Chapter 17) on an anonymous method, and calling GetType() is possible only after assigning or casting the anonymous method to a delegate variable.
TODO: internals
Closure—a data structure (class in C#) that contains an expression and the variables (public fields in C#) necessary to evaluate the expression.
Expression Trees
Used to store lambda expressions as data for passing or translating.
Expression trees are object graphs
composed of read-only collection of parameters, a return type, and a body– which is another expression.
Lambda Expressions vs. Expression Trees
compiled into a delegate in CIL
compiled into a data structure of type “System.Linq.Expressions.Expression”
usingSystem;classDelegateSample{publicstaticvoidBubbleSort(int[]items,ComparisonHandlercomparisonMethod){inti;intj;inttemp;if(items==null)return;if(comparisonMethod==null)thrownewArgumentNullException("comparisonMethod");for(i=items.Length-1;i>=0;i--){for(j=1;j<=i;j++){if(comparisonMethod(items[j-1],items[j])){temp=items[j-1];items[j-1]=items[j];items[j]=temp;}}}}publicdelegateboolComparisonHandler(intfirst,intsecond);publicstaticboolGreaterThan(intfirst,intsecond){returnfirst>second;}publicstaticboolAlphabeticalGreaterThan(intfirst,intsecond){intcomparison;comparison=(first.ToString().CompareTo(second.ToString()));returncomparison>0;}staticvoidMain(){int[]items=newint[100];Randomrandom=newRandom();for(inti=0;i<items.Length;i++){items[i]=random.Next(0,100);}BubbleSort(items,GreaterThan);// In C# 2.0, we can also use BubbleSort(items, new ComparisonHandler(GreaterThan))for(inti=0;i<items.Length;i++){Console.WriteLine(items[i]);}Console.ReadLine();}}
13 Events 507
publish-subscribe design pattern
Coding the Observer Pattern with Multicast Delegates (with operator+, +=, …)
See the examples below
TODO: P518 Multicast Delegate Internals
How to handle exceptions from subscribers?
foreach(TDelegate handler in delegates.GetInvocationList())See examples below
How to handle multiple returns from multicast delegates?
Also foreach(TDelegate handler in delegates.GetInvocationList())
Events
introduced to overcome 2 delegate shortages
Encapsulating the Subscription
User may use = instead of += by mistake
Encapsulating the publication
Delegates may be invoked outside the containing class
easy to forget to check for null before invoking the delegate
event keyword before the delegate type and follows a empty delegate assignment
e.g. public event TemperatureChangeHandler OnTemperatureChange = delegate { };
event ensures that any reassignment of the delegate could occur only from within the class.
an empty delegate delegate {} represents a collection of zero listeners.
// event implementation of subscriber-publisherusingSystem;// Heater and Cooler Event Subscriber ImplementationsclassCooler{publicCooler(floattemperature){Temperature=temperature;}publicfloatTemperature{get{return_Temperature;}set{_Temperature=value;}}privatefloat_Temperature;publicvoidOnTemperatureChanged(objectsender,Thermostat.TemperatureArgsargs){if(args.NewTemperature>Temperature){System.Console.WriteLine("Cooler: On");}else{System.Console.WriteLine("Cooler: Off");}}}classHeater{publicHeater(floattemperature){Temperature=temperature;}publicfloatTemperature{get{return_Temperature;}set{_Temperature=value;}}privatefloat_Temperature;publicvoidOnTemperatureChanged(objectsender,Thermostat.TemperatureArgsargs){if(args.NewTemperature<Temperature){System.Console.WriteLine("Heater: On");}else{System.Console.WriteLine("Heater: Off");}}}// Defining the Event Publisher, `Thermostat`publicclassThermostat{publicclassTemperatureArgs:System.EventArgs{// conventionspublicTemperatureArgs(floatnewTemperature){NewTemperature=newTemperature;}publicfloatNewTemperature{get{return_newTemperature;}set{_newTemperature=value;}}privatefloat_newTemperature;}// Define the delegate data type// It is a norm:// sender: reference of the to the object that invoke the delegate // args: if of type `System.EventArgs` or derives from `System.EventArgs` but contains additional datapublicdelegatevoidTemperatureChangeHandler(objectsender,TemperatureArgsnewTemperature);// Define the event publisher// delegate类型之前加event,赋一个空的delegate// an empty delegate represents a collection of zero listenerspubliceventTemperatureChangeHandlerOnTemperatureChange=delegate{};publicfloatCurrentTemperature{get{return_CurrentTemperature;}set{if(value!=CurrentTemperature){_CurrentTemperature=value;// If there are any subscribers// then notify them of changes in // temperatureif(OnTemperatureChange!=null){// Call subscribersOnTemperatureChange(this,newTemperatureArgs(value));}}}}privatefloat_CurrentTemperature;}classProgram{publicstaticvoidMain(){Thermostatthermostat=newThermostat();Heaterheater=newHeater(60);Coolercooler=newCooler(80);stringtemperature;// Register subscribersthermostat.OnTemperatureChange+=heater.OnTemperatureChanged;thermostat.OnTemperatureChange+=cooler.OnTemperatureChanged;Console.Write("Enter temperature: ");temperature=Console.ReadLine();thermostat.CurrentTemperature=int.Parse(temperature);}}
12345678910111213
// Define the event publisherpubliceventTemperatureChangeHandlerOnTemperatureChange{add{System.Delegate.Remove(_OnTemperatureChange,value);}remove{System.Delegate.Combine(value,_OnTemperatureChange);}}protectedTemperatureChangeHandler_OnTemperatureChange;