C# For Java Programmer (Language Basic)

It’s based on C# 7.0, which is the latest version with VS 2017. I list the feature unfamiliar to Java programmer.

Question Answer
01.Losing precision:convert int to float and again back to int floating-point has less precision than integer. Double has NO such issue.
02.Rounding errors from float and double Both are in base 2, so can lose precision for base 10 figures. decimal is base 10, but 10 times slower due to the lack of native support of CPU.
03.Matrix (vs jagged arrays) (see code) At least one int[,] can be omitted, but not both. Jagged arrays have the syntax like int[][].
04.ref and out parameter out differs from ref modifier in that out arguments can be declared when being used in method invocation.
05.Named arguments and optional parameters For methods has several optional parameters, use named arguments for the sake of clarity.
06.Null operators ? and ?? ? turns an expression’s result to Option, while ?? does one more step further to call orElseGet method with laziness enabled.
07.switch with patterns switch supports types and null (C# 7, see code)
08.Renaming imported types and namespaces using NewType1 = Some.Namespace.Type1 and using NewNamespace = Some.Namespace
09.One-line expression syntax int triple(int x) => x*3
10.Overloading constructors Person(String name, int age) : this(name) { ... } For subclass, use base() and it enforces constructor in super class is invoked first.
11.Deconstructor var (firstName, lastName) = aPersonName (C# 7, see code)
12.Object initializers Constructor has an advantage over object initializer in that for immutable object, fields/properties can be declared as read-only and initialized in constructor (see code)
13.Indexers string this [int index] { get {...} set {...}}
14.Static constructors static PersonName() {...} Acted as static block which get executed once per type after static field initializers.
15.nameof operator string foo = nameof (aPersonName) foo will be updated after renaming aPersonName
16.is operator Useful to have a casted variable even in an outer scope (see code)
17.Hiding inherited members instead of override public new void Foo() {...} (see code)
18.Initialization order 1.Fields in subclass, 2.Arguments to base class constructor, 3.Fields in base class, 4.base class constructor, 5.subclass constructor.
19.virtual, override and sealed virtual and override make the method in subclass override the one in base class (see code)
20.reimplement (new) reimplemented method visible in subclass ONLY, also note without visual subclass can’t override (see code)
21.explicit implement It solves method conflict from multiple interfaces by asking caller to cast instance to interface to be able to call the method. Otherwise the implementation is INVISIBLE in the class itself (see code)
22.class VS interface Use (sub)classes when they naturally share an implementation; use interface when they have independent implementations.
23.Covariance VS contravariance Covariance (out) is generic type used in return value, means IPopable<Animal> obj = instance; // instance is IPopable<Cat> because Cat is instance of Animal. Contravariance (in) is generic type used in parameters, means IPushable<Cat> obj = instance; // instance is IPushable<Animal> because Cat can always be used as method argument when parameter type is Animal (see code)
24.delegate as Func and Action in System namespace delegate acts as class (generated by compiler) captured surrounding variables, used like public delegate void Foo(); Foo aDelegate = methodName, aDelegate += anotherMethodName Note Foo can be declared by System.Action, which is delegate void Action();. Also note delegate can be multi cast.
25.Standard event pattern 1.MyEventArgs : System.EventArgs, which is marker class. 2.Use generic delegate System.EventHandler and method to fire event (see code)
26.Variables captured by delegate Not evaluated until the delegate is actually invoked. (see code)
27.Local method VS lambda expression int Cube(int x) => x*x*x is local method is better in that 1.can be recursive, 2.slightly faster
28.catch clause 1.No exception variable, 2.Omit type, 3.exception filter (see code)
29.throw expression Since C# 7, throw can be as expression like string Foo() => throw new NotImplementedException();
30.Trap of rethrow To rethrow the original exception (with StackTrace kept), throw instead of throw ex. Rethrow can be used not to leak technical details by rethrowing a less specific exception.
31.TryXXX pattern Have a TryXXX method and let XXX method call TryXXX (see code)
32.foreach translation It uses enumerator (instead of enumerable) to travel through (see code)
33.Collection initializer Two kinds: normal and indexed (see code)
34.Iterator With return value as IEnumerable, use yield return t (or yield break) inside a method and compiler translates the method into MoveNext method and Current property.
35.Iterator and try…catch and IDisposable yield return doesn’t work well with try…cach, only try…finally. Also note foreach can dispose enumerator in case of early break. For explicitly using enumerator (and having early break), wrap it with using.
36.Nullable It works for value type (struct) to have a way to define “empty” and compiler can lift operators to nullable from underlying value type (see code)
37.Cast null to a specific nullable int? i = (int?)null
38.Get caller’s info System.Runtime.CompilerServices (see code)
39.string’s == and Equals immutable string’s == operator has been overloaded to behave like its Equals method.
40.GetHashCode for more than two fields (see code)
41.CompareTo and Equals for case-insensitive string Begin with if (Equals(other)) return 0; for CompareTo so that equal strings always return early.
42.Functional Construction Build structural objects (see code)
43.Disposable pattern (see code)
44.Null conditional operator and index operator ICouldBeNull?[0] == 'S' Apart from ., index operator can come after null conditional operator.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
// 03
int[,] matrix = new int[,]
{
{1,2,3},
{2,4,6},
{1,4,8},
};

// 07
switch (age) {
case int child when child < 18:
...
break;
case null:
...
break;
}

// 11
class PersonName {
...
public void Deconstruct (out string firstName, out string lastName) {
firstName = givenName;
lastName = familyName;
}
...
}

// 12
Person p1 = new Person("John Doe") { Age = 21 };
Person p2 = new Person("Tom Zhang", 35);

// 16
if (foo is Bar bar && bar.Size > 0)
...
Console.WriteLine (bar.Name); // bar still in scope

// 17
Subclass sub = new Subclass();
sub.Foo(); // Foo from subclass
Superclass superClass = sub;
superClass.Foo(); // Foo from super class

// 19
interface ICanFly {
void FlyAndCry();
}

class Bird : ICanFly {
public virtual void FlyAndCry() { WriteLine("Bird's cry"); }
}

class Falcon : Bird {
public sealed override void FlyAndCry() { WriteLine("Falcon !!!"); }
}

class SonOfFalcon : Falcon {
// compile error because of sealed attribute in Falcon::FlyAndCry
public override void FlyAndCry() { WriteLine("Son of Falcon"); }
}

Falcon f = new Falcon();
f.FlyAndCry(); // Falcon !!!
(f as Bird).FlyAndCry(); // Falcon !!!
(f as ICanFly).FlyAndCry(); // Falcon !!!

// 20
interface ICanFly {
void FlyAndCry();
}

class Bird : ICanFly {
// virtual doesn't matter in this case
// but if omitted, it acts like sealed and override is NOT allowed in subclass
public virtual void FlyAndCry() { WriteLine("Bird's cry"); }
}

class Falcon : Bird {
// compiler warning if new is omitted
public new void FlyAndCry() { WriteLine("Falcon !!!"); }
}

Falcon f = new Falcon();
f.FlyAndCry(); // Falcon !!!
(f as Bird).FlyAndCry(); // Bird's cry
(f as ICanFly).FlyAndCry(); // Bird's cry

// 21
interface ICanFly {
void FlyAndCry();
}

class Bird : ICanFly {
public void FlyAndCry() { WriteLine("Bird's cry"); }
}

class Falcon : Bird, ICanFly { // the interface also needs to be delcared explicitly
// attributes like public, new or override not allowed for explicit implement
void ICanFly.FlyAndCry() { WriteLine("Falcon !!!"); }
}

Falcon f = new Falcon();
f.FlyAndCry(); // Bird's cry
(f as Bird).FlyAndCry(); // Bird's cry
(f as ICanFly).FlyAndCry(); // Falcon !!!

// 22
class Animal { }
class Cat : Animal { }

interface IPopable<out R> { R Pop(); }
interface IPushable<in T> { void Push(T t); }

class MyStack<T> : IPushable<T>, IPopable<T> {
public T Pop() { WriteLine("Pop " + typeof(T)); return default(T); }
public void Push(T t) { WriteLine("Push " + typeof(T)); }
}

IPushable<Cat> stack = new MyStack<Animal>();
stack.Push(new Cat());

IPopable<Animal> stack2 = new MyStack<Cat>();
WriteLine(stack2.Pop());

// 25
public class PriceChangedEventArgs : EventArgs {
public readonly decimal OldPrice;
public readonly decimal NewPrice;
public PriceChangedEventArgs(decimal oldValue, decimal newValue) {
OldPrice = oldValue;
NewPrice = newValue;
}
}

public class Stock {
decimal price;
public event EventHandler<PriceChangedEventArgs> PriceChagned;

// Note null-conditional operator, which is thread-safe.
protected virtual void OnPriceChanged(PriceChangedEventArgs e) { PriceChagned?.Invoke(this, e); }

public decimal Price {
get { return price; }
set {
if (price == value) return;
decimal oldValue = price;
price = value;
OnPriceChanged(new PriceChangedEventArgs(oldValue, price));
}
}
}

Stock stock = new Stock();
stock.Price = 20m;
// Note local method is better than the following EventHandler, in that putting name into lambda type makes code more readable
// EventHandler<PriceChangedEventArgs> printPrice = (o, e) => WriteLine($"old price = {e.OldPrice}, new price = {e.NewPrice}");
void printPrice(object o, PriceChangedEventArgs e) => WriteLine($"old price = {e.OldPrice}, new price = {e.NewPrice}");
stock.PriceChagned += printPrice;
stock.Price = 25m; // printPrice works
stock.Price = 30m; // printPrice works
stock.PriceChagned -= printPrice;
stock.Price = 10m; // printPrice doesn't work anymore

// 26
Action[] actions = new Action[3];
for (int i = 0; i < 3; i++)
actions[i] = () => Write(i);
foreach (var a in actions) a();
// 333 because i is captured as variable outside the loop, which means the same variable captured in each iteration.

int i = 0;
foreach (char c in "abc")
actions[i++] = () => WriteLine(c);
foreach (var a in actions) a();
// abc instead of ccc because since C# 5, variable declared in foreach is captured as separate variables.

// 28
catch (OverflowException) {...}
catch {...}
catch (WebException e) when (e.Status == WebExceptionStatus.Timeout) {...}

// 31
R XXX (T input) {
R result;
if (!TryXXX(input, out result))
throw new XXXException(...);
return result;
}

// 32
foreach (char c in "bear")
Console.WriteLine(c);

// translation
using (var enumerator = "bear".GetEnumerator())
while (enumerator.MoveNext()) {
var element = enumerator.Current;
Console.WriteLine(element);
}

// 33
var lst = new List<int> {1,2,3};
var dict = new Dictionary<int,string>() {
[1] = "one",
[2] = "two"
};

// 36
object o = null; // null can be assigned to reference type or nullable value type
int? i = o as int?;
int? j = 2;
bool b = i < j; // lift operator: (i.HasValue && j.HasValue) ? (i.Value < j.Value) : false

// 38
public static void Log(
[CallerFilePath] string path = null,
[CallerMemberName] string caller = null,
[CallerLineNumber] int? line = null
) { WriteLine($"{caller} calls in at line {line} from {path}"); }

// 40
int hash = 17;
hash = hash*31 + field1.GetHashCode();
hash = hash*31 + field2.GetHashCode();
hash = hash*31 + field3.GetHashCode();

// 42
XElement customer =
new XElement("customer", new XAttribute("id", 123),
new XElement("firstname", "joe"),
new XElement("lastname", "doe",
new XComment("a common name")
)
);

/*
<customer id=123>
<firstname>joe</firstname>
<lastname>doe<!--a common name--></lastname>
</customer>
*/

// 43
class Test : IDisposable {
public void Dispose() { // NOT virtual
Dispose(true);
GC.SuppressFinalize(this); // prevent finalizer from running
}
protected virtual void Dispose(bool disposing) {
if (disposing) {
// call Dispose on other objects owned by this instance
}
// release unmanaged resources owned by (just) this instance
}
~Test() {
Dispose(false); // false because other objects could have been finalized
}
}