using System;namespaceTutorial{classProgram{staticvoidswap(objecta,objectb){
Console.WriteLine($"{a}{b}");// world helloobjectc;c=a;a=b;b=c;
Console.WriteLine($"{a}{b}");// hello world}staticvoidMain(string[]args){strings="world";stringt="hello";
Console.WriteLine($"{s}{t}");// world hello
swap(s, t);
Console.WriteLine($"{s}{t}");// world hello}}}
Value parameters are just copies of their respective arguments
In the above example, the object references are copied
a and s share the same string object
Assignments to value parameters are invisible to the caller
a is rebound, but s is not
Mutation througha would be noticeable through s
But object has no mutator methods
And strings are immutable
ref parameters alias their respective argument variables:
using System;namespaceTutorial{classProgram{staticvoidswap(refobjecta,refobjectb){objectc;c=a;a=b;b=c;}staticvoidMain(string[]args){strings="world";stringt="hello";// cannot convert from "ref string" to "ref object"
swap(ref s,ref t);}}}
In expected OOP fashion, strings are objects
But stringvariables are not objectvariables
Otherwise, we could rebind string variables to non-string objects:
strings="hi";refobjecto=ref s;// cannot convert from "ref string" to "ref object"o=42;
using System;namespaceTutorial{structDuration{// Constants are not stored anywhere at runtime.// Usages compile away to their actual value.publicconstintSECONDS_PER_MINUTE=60;publicconstintSECONDS_PER_HOUR=60*60;privatelong_seconds;publiclongSeconds{get=> _seconds;set=>_seconds=value;}publiclongMinutes{get{return_seconds/SECONDS_PER_MINUTE;}set{if(value*SECONDS_PER_MINUTE/SECONDS_PER_MINUTE!=value)thrownew ArgumentException($"{value} is too big");_seconds=value*SECONDS_PER_MINUTE;}}publiclongHours{get=>_seconds/SECONDS_PER_HOUR;set{if(value*SECONDS_PER_HOUR/SECONDS_PER_HOUR!=value)thrownew ArgumentException($"{value} is too big");_seconds=value*SECONDS_PER_HOUR;}}}classProgram{staticvoidMain(string[]args){Durationdur=new Duration();
dur.Hours =2;// 120 minutes is 7200 seconds
Console.WriteLine($"{dur.Minutes} minutes is {dur.Seconds} seconds");}}}
Object-oriented programming
extends C implements I, J, K translates to : C, I, J, K
The default is : object
super(...); or this(...); translate to : base(...) or : this(...)
The default is : base()
Default constructor is generated in the absence of explicit constructors
Only abstract and virtual methods are overridable
Overriding requires override keyword
Missing override does not compile, unlike Java @Override
override interface method not compile, unlike Java @Override
using System;using System.Collections.Generic;namespaceTutorial{classProgram{staticIEnumerable<double>fixCos(){doublex=0;do{yieldreturnx;}while(x!=(x= Math.Cos(x)));}staticIEnumerable<double>fixSqrt(){doublex=0.5;do{yieldreturnx;}while(x!=(x= Math.Sqrt(x)));}staticvoidMain(string[]args){foreach(double x in fixCos()){
Console.WriteLine(x);// 0// 1// 0.5403023058681397// 0.8575532158463934// 0.6542897904977791// 0.7934803587425656// 0.7013687736227565// ...// 0.7390851332151608// 0.7390851332151606// 0.7390851332151607}foreach(double x in fixSqrt()){
Console.WriteLine(x);// 0.5// 0.7071067811865476// 0.8408964152537146// 0.9170040432046712// 0.9576032806985736// 0.9785720620877001// 0.9892280131939755// ...// 0.9999999999999997// 0.9999999999999998// 0.9999999999999999 }}}}
Can we refactor fixCos and fixSqrt into one method?
Abstract over initial number (0 or 0.5) with double x parameter
Abstract over applied method (Math.Cos or Math.Sqrt) with delegate parameter:
using System;using System.Collections.Generic;namespaceTutorial{classProgram{delegatedouble F(doublex);staticIEnumerable<double>fix(Ff,doublex){do{yieldreturnx;}while(x!=(x= f(x)));}staticvoidMain(string[]args){foreach(double x in fix(Math.Cos,0)){
Console.WriteLine(x);}foreach(double x in fix(Math.Sqrt,0.5)){
Console.WriteLine(x);}}}}
Function delegates are already provided by the standard library:
using System;using System.Threading;namespaceTutorial{classSubjectExample{publicAction<DateTime>HellFrozeOver;publicvoidFreezeHell(){
Console.Write("Freezing hell... ");
Thread.Sleep(2000);
Console.WriteLine("done!");if(HellFrozeOver!=null){
HellFrozeOver(DateTime.Now);}}}classObserverExample{publicvoidLog(DateTimewhen){
Console.WriteLine("Hell froze over on "+when);}}classProgram{staticvoidMain(string[]args){varsubject=new SubjectExample();varfirst=new ObserverExample();varsecond=new ObserverExample();
subject.HellFrozeOver += first.Log;
subject.HellFrozeOver += second.Log;
subject.FreezeHell();
Console.WriteLine("--------------------------------------");varthird=new ObserverExample();// Whoops, accidental = instead of +=
subject.HellFrozeOver = third.Log;// Whoops, delegate invocation outside of subject
subject.HellFrozeOver(new DateTime(1912,4,15));}}}
The event keyword prevents such accidental misuse:
classSubjectExample{publiceventAction<DateTime>HellFrozeOver;publicvoidFreezeHell(){// ...}}// The event SubjectExample.HellFrozeOver can only appear// on the left hand side of += or -=// (except when used from within the type SubjectExample)
subject.HellFrozeOver = third.Log;// ditto
subject.HellFrozeOver(new DateTime(1912,4,15));