C# is an elegant and type-safe object-oriented language that enables developers to build a variety of secure and robust applications that run on the cross-platform .NET framework.
1// Single-line comments start with //
2
3/*
4Multi-line comments look like this
5*/
6
7/// <summary>
8/// This is an XML documentation comment which can be used to generate external
9/// documentation or provide context help within an IDE
10/// </summary>
11/// <param name="firstParam">This is some parameter documentation for firstParam</param>
12/// <returns>Information on the returned value of a function</returns>
13public void MethodOrClassOrOtherWithParsableHelp(string firstParam) { }
14
15// Specify the namespaces this source code will be using
16// The namespaces below are all part of the standard .NET Framework Class Library
17using System;
18using System.Collections.Generic;
19using System.Dynamic;
20using System.Linq;
21using System.Net;
22using System.Threading.Tasks;
23using System.IO;
24
25// But this one is not:
26using System.Data.Entity;
27// In order to be able to use it, you need to add a dll reference
28// This can be done with the NuGet package manager: `Install-Package EntityFramework`
29
30// Namespaces define scope to organize code into "packages" or "modules"
31// Using this code from another source file: using Learning.CSharp;
32
33// You can also do this in C# 10, it is called file-scoped namespaces.
34// namespace Learning.CSharp;
35
36namespace Learning.CSharp
37{
38 // Each .cs file should at least contain a class with the same name as the file.
39 // You're allowed to do otherwise, but shouldn't for sanity.
40 public class LearnCSharp
41 {
42 // BASIC SYNTAX - skip to INTERESTING FEATURES if you have used Java or C++ before
43 public static void Syntax()
44 {
45 // Use Console.WriteLine to print lines
46 Console.WriteLine("Hello World");
47 Console.WriteLine(
48 "Integer: " + 10 +
49 " Double: " + 3.14 +
50 " Boolean: " + true);
51
52 // To print without a new line, use Console.Write
53 Console.Write("Hello ");
54 Console.Write("World");
55
56 ///////////////////////////////////////////////////
57 // Types & Variables
58 //
59 // Declare a variable using <type> <name>
60 ///////////////////////////////////////////////////
61
62 // Sbyte - Signed 8-bit integer
63 // (-128 <= sbyte <= 127)
64 sbyte fooSbyte = 100;
65
66 // Byte - Unsigned 8-bit integer
67 // (0 <= byte <= 255)
68 byte fooByte = 100;
69
70 // Short - 16-bit integer
71 // Signed - (-32,768 <= short <= 32,767)
72 // Unsigned - (0 <= ushort <= 65,535)
73 short fooShort = 10000;
74 ushort fooUshort = 10000;
75
76 // Integer - 32-bit integer
77 int fooInt = 1; // (-2,147,483,648 <= int <= 2,147,483,647)
78 uint fooUint = 1; // (0 <= uint <= 4,294,967,295)
79
80 // Long - 64-bit integer
81 long fooLong = 100000L; // (-9,223,372,036,854,775,808 <= long <= 9,223,372,036,854,775,807)
82 ulong fooUlong = 100000L; // (0 <= ulong <= 18,446,744,073,709,551,615)
83 // Numbers default to being int or uint depending on size.
84 // L is used to denote that this variable value is of type long or ulong
85
86 // Double - Double-precision 64-bit IEEE 754 Floating Point
87 double fooDouble = 123.4; // Precision: 15-16 digits
88
89 // Float - Single-precision 32-bit IEEE 754 Floating Point
90 float fooFloat = 234.5f; // Precision: 7 digits
91 // f is used to denote that this variable value is of type float
92
93 // Decimal - a 128-bits data type, with more precision than other floating-point types,
94 // suited for financial and monetary calculations
95 decimal fooDecimal = 150.3m;
96
97 // Boolean - true & false
98 bool fooBoolean = true; // or false
99
100 // Char - A single 16-bit Unicode character
101 char fooChar = 'A';
102
103 // Strings -- unlike the previous base types which are all value types,
104 // a string is a reference type. That is, you can set it to null
105 string fooString = "\"escape\" quotes and add \n (new lines) and \t (tabs)";
106 Console.WriteLine(fooString);
107
108 // You can access each character of the string with an indexer:
109 char charFromString = fooString[1]; // => 'e'
110 // Strings are immutable: you can't do fooString[1] = 'X';
111
112 // Compare strings with current culture, ignoring case
113 string.Compare(fooString, "x", StringComparison.CurrentCultureIgnoreCase);
114
115 // Formatting, based on sprintf
116 string fooFs = string.Format("Check Check, {0} {1}, {0} {1:0.0}", 1, 2);
117
118 // Dates & Formatting
119 DateTime fooDate = DateTime.Now;
120 Console.WriteLine(fooDate.ToString("hh:mm, dd MMM yyyy"));
121
122 // Verbatim String
123 // You can use the @ symbol before a string literal to escape all characters in the string
124 string path = "C:\\Users\\User\\Desktop";
125 string verbatimPath = @"C:\Users\User\Desktop";
126 Console.WriteLine(path == verbatimPath); // => true
127
128 // You can split a string over two lines with the @ symbol. To escape " use ""
129 string bazString = @"Here's some stuff
130on a new line! ""Wow!"", the masses cried";
131
132 // Use const or read-only to make a variable immutable
133 // const values are calculated at compile time
134 const int HoursWorkPerWeek = 9001;
135
136 ///////////////////////////////////////////////////
137 // Data Structures
138 ///////////////////////////////////////////////////
139
140 // Arrays - zero indexed
141 // The array size must be decided upon declaration
142 // The format for declaring an array is
143 // <datatype>[] <var name> = new <datatype>[<array size>];
144 int[] intArray = new int[10];
145
146 // Another way to declare & initialize an array
147 int[] y = { 9000, 1000, 1337 };
148
149 // Indexing an array - Accessing an element
150 Console.WriteLine("intArray @ 0: " + intArray[0]);
151 // Arrays are mutable.
152 intArray[1] = 1;
153
154 // Lists
155 // Lists are used more frequently than arrays as they are more flexible
156 // The format for declaring a list is
157 // List<datatype> <var name> = new List<datatype>();
158 List<int> intList = new List<int>();
159 List<string> stringList = new List<string>();
160 List<int> z = new List<int> { 9000, 1000, 1337 }; // initialize
161 // The <> are for generics - Check out the cool stuff section
162
163 // Lists don't default to a value;
164 // A value must be added before accessing the index
165 intList.Add(1);
166 Console.WriteLine("intList at 0: " + intList[0]);
167
168 // Other data structures to check out:
169 // Stack/Queue
170 // Dictionary (an implementation of a hash map)
171 // HashSet
172 // Read-only Collections
173 // Tuple (.NET 4+)
174
175 ///////////////////////////////////////
176 // Operators
177 ///////////////////////////////////////
178 Console.WriteLine("\n->Operators");
179
180 int i1 = 1, i2 = 2; // Shorthand for multiple declarations
181
182 // Arithmetic is straightforward
183 Console.WriteLine(i1 + i2 - i1 * 3 / 7); // => 3
184
185 // Modulo
186 Console.WriteLine("11%3 = " + (11 % 3)); // => 2
187
188 // Comparison operators
189 Console.WriteLine("3 == 2? " + (3 == 2)); // => false
190 Console.WriteLine("3 != 2? " + (3 != 2)); // => true
191 Console.WriteLine("3 > 2? " + (3 > 2)); // => true
192 Console.WriteLine("3 < 2? " + (3 < 2)); // => false
193 Console.WriteLine("2 <= 2? " + (2 <= 2)); // => true
194 Console.WriteLine("2 >= 2? " + (2 >= 2)); // => true
195
196 // Bitwise operators!
197 /*
198 ~ Unary bitwise complement
199 << Signed left shift
200 >> Signed right shift
201 & Bitwise AND
202 ^ Bitwise exclusive OR
203 | Bitwise inclusive OR
204 */
205
206 // Incrementing
207 int i = 0;
208 Console.WriteLine("\n->Inc/Dec-rement");
209 Console.WriteLine(i++); //Prints "0", i = 1. Post-Increment
210 Console.WriteLine(++i); //Prints "2", i = 2. Pre-Increment
211 Console.WriteLine(i--); //Prints "2", i = 1. Post-Decrement
212 Console.WriteLine(--i); //Prints "0", i = 0. Pre-Decrement
213
214 ///////////////////////////////////////
215 // Control Structures
216 ///////////////////////////////////////
217 Console.WriteLine("\n->Control Structures");
218
219 // If statements are C-like
220 int j = 10;
221 if (j == 10)
222 {
223 Console.WriteLine("I get printed");
224 }
225 else if (j > 10)
226 {
227 Console.WriteLine("I don't");
228 }
229 else
230 {
231 Console.WriteLine("I also don't");
232 }
233
234 // Ternary operators
235 // A simple if/else can be written as follows
236 // <condition> ? <true> : <false>
237 int toCompare = 17;
238 string isTrue = toCompare == 17 ? "True" : "False";
239
240 // While loop
241 int fooWhile = 0;
242 while (fooWhile < 100)
243 {
244 // Iterated 100 times, fooWhile 0->99
245 fooWhile++;
246 }
247
248 // Do While Loop
249 int fooDoWhile = 0;
250 do
251 {
252 // Start iteration 100 times, fooDoWhile 0->99
253 if (false)
254 continue; // skip the current iteration
255
256 fooDoWhile++;
257
258 if (fooDoWhile == 50)
259 break; // breaks from the loop completely
260
261 } while (fooDoWhile < 100);
262
263 // for loop structure => for(<start_statement>; <conditional>; <step>)
264 for (int fooFor = 0; fooFor < 10; fooFor++)
265 {
266 // Iterated 10 times, fooFor 0->9
267 }
268
269 // For Each Loop
270 // foreach loop structure => foreach(<iteratorType> <iteratorName> in <enumerable>)
271 // The foreach loop loops over any object implementing IEnumerable or IEnumerable<T>
272 // All the collection types (Array, List, Dictionary...) in the .NET framework
273 // implement one or both of these interfaces.
274 // (The ToCharArray() could be removed, because a string also implements IEnumerable)
275 foreach (char character in "Hello World".ToCharArray())
276 {
277 // Iterated over all the characters in the string
278 }
279
280 // Switch Case
281 // A switch works with byte, short, char, and int data types.
282 // It also works with enumerated types (discussed in Enum Types),
283 // the String class, and a few special classes that wrap
284 // primitive types: Character, Byte, Short, and Integer.
285 int month = 3;
286 string monthString;
287 switch (month)
288 {
289 case 1:
290 monthString = "January";
291 break;
292 case 2:
293 monthString = "February";
294 break;
295 case 3:
296 monthString = "March";
297 break;
298 // You can assign more than one case to an action
299 // But you can't add an action without a break before another case
300 // (if you want to do this, you would have to explicitly add a goto case x)
301 case 6:
302 case 7:
303 case 8:
304 monthString = "Summer time!!";
305 break;
306 default:
307 monthString = "Some other month";
308 break;
309 }
310
311 ///////////////////////////////////////
312 // Converting Data Types And Typecasting
313 ///////////////////////////////////////
314
315 // Converting data
316
317 // Convert String To Integer
318 // this will throw a FormatException on failure
319 int.Parse("123"); // returns an integer version of "123"
320
321 // TryParse will default to the type's default value on failure
322 // in this case 0
323 int tryInt;
324 if (int.TryParse("123", out tryInt)) // Function is boolean
325 Console.WriteLine(tryInt); // 123
326
327 // Convert Integer To String
328 // The Convert class has a number of methods to facilitate conversions
329
330 // String to int
331
332 // Better
333 bool result = int.TryParse(string, out var integer)
334 int.Parse(string);
335
336 // Not recommended
337 Convert.ToString(123);
338
339 // Int to string
340 tryInt.ToString();
341
342 // Casting
343 // Cast decimal 15 to an int
344 // and then implicitly cast to long
345 long x = (int) 15M;
346 }
347
348 ///////////////////////////////////////
349 // CLASSES - see definitions at end of file
350 ///////////////////////////////////////
351 public static void Classes()
352 {
353 // See Declaration of objects at end of file
354
355 // Use new to instantiate a class
356 Bicycle trek = new Bicycle();
357
358 // Call object methods
359 trek.SpeedUp(3); // You should always use setter and getter methods
360 trek.Cadence = 100;
361
362 // ToString is a convention to display the value of this Object.
363 Console.WriteLine("trek info: " + trek.Info());
364
365 // Instantiate a new Penny Farthing
366 PennyFarthing funbike = new PennyFarthing(1, 10);
367 Console.WriteLine("funbike info: " + funbike.Info());
368
369 Console.Read();
370 } // End main method
371
372 // Available in C# 9 and later, this is basically syntactic sugar for a class. Records are immutable*.
373 public record ARecord(string Csharp);
374
375 // CONSOLE ENTRY - A console application must have a main method as an entry point
376 public static void Main(string[] args)
377 {
378 OtherInterestingFeatures();
379 }
380
381 //
382 // INTERESTING FEATURES
383 //
384
385 // DEFAULT METHOD SIGNATURES
386
387 public // Visibility
388 static // Allows for direct call on class without object
389 int // Return Type,
390 MethodSignatures(
391 int maxCount, // First variable, expects an int
392 int count = 0, // will default the value to 0 if not passed in
393 int another = 3,
394 params string[] otherParams // captures all other parameters passed to method
395 )
396 {
397 return -1;
398 }
399
400 // Methods can have the same name, as long as the signature is unique
401 // A method that differs only in return type is not unique
402 public static void MethodSignatures(
403 ref int maxCount, // Pass by reference
404 out int count)
405 {
406 // the argument passed in as 'count' will hold the value of 15 outside of this function
407 count = 15; // out param must be assigned before control leaves the method
408 }
409
410 // GENERICS
411 // The classes for TKey and TValue is specified by the user calling this function.
412 // This method emulates Python's dict.setdefault()
413 public static TValue SetDefault<TKey, TValue>(
414 IDictionary<TKey, TValue> dictionary,
415 TKey key,
416 TValue defaultItem)
417 {
418 TValue result;
419 if (!dictionary.TryGetValue(key, out result))
420 return dictionary[key] = defaultItem;
421 return result;
422 }
423
424 // You can narrow down the objects that are passed in
425 public static void IterateAndPrint<T>(T toPrint) where T: IEnumerable<int>
426 {
427 // We can iterate, since T is a IEnumerable
428 foreach (var item in toPrint)
429 // Item is an int
430 Console.WriteLine(item.ToString());
431 }
432
433 // YIELD
434 // Usage of the "yield" keyword indicates that the method it appears in is an Iterator
435 // (this means you can use it in a foreach loop)
436 public static IEnumerable<int> YieldCounter(int limit = 10)
437 {
438 for (var i = 0; i < limit; i++)
439 yield return i;
440 }
441
442 // which you would call like this :
443 public static void PrintYieldCounterToConsole()
444 {
445 foreach (var counter in YieldCounter())
446 Console.WriteLine(counter);
447 }
448
449 // you can use more than one "yield return" in a method
450 public static IEnumerable<int> ManyYieldCounter()
451 {
452 yield return 0;
453 yield return 1;
454 yield return 2;
455 yield return 3;
456 }
457
458 // you can also use "yield break" to stop the Iterator
459 // this method would only return half of the values from 0 to limit.
460 public static IEnumerable<int> YieldCounterWithBreak(int limit = 10)
461 {
462 for (var i = 0; i < limit; i++)
463 {
464 if (i > limit/2) yield break;
465 yield return i;
466 }
467 }
468
469 public static void OtherInterestingFeatures()
470 {
471 // OPTIONAL PARAMETERS
472 MethodSignatures(3, 1, 3, "Some", "Extra", "Strings");
473 MethodSignatures(3, another: 3); // explicitly set a parameter, skipping optional ones
474
475 // BY REF AND OUT PARAMETERS
476 int maxCount = 0, count; // ref params must have value
477 MethodSignatures(ref maxCount, out count);
478
479 // EXTENSION METHODS
480 int i = 3;
481 i.Print(); // Defined below
482
483 // NULLABLE TYPES - great for database interaction / return values
484 // any value type (i.e. not a class) can be made nullable by suffixing a ?
485 // <type>? <var name> = <value>
486 int? nullable = null; // short hand for Nullable<int>
487 Console.WriteLine("Nullable variable: " + nullable);
488 bool hasValue = nullable.HasValue; // true if not null
489
490 // ?? is syntactic sugar for specifying default value (coalesce)
491 // in case variable is null
492 int notNullable = nullable ?? 0; // 0
493
494 // ?. is an operator for null-propagation - a shorthand way of checking for null
495 nullable?.Print(); // Use the Print() extension method if nullable isn't null
496
497 // IMPLICITLY TYPED VARIABLES - you can let the compiler work out what the type is:
498 var magic = "magic is a string, at compile time, so you still get type safety";
499 // magic = 9; will not work as magic is a string, not an int
500
501 // GENERICS
502 //
503 var phonebook = new Dictionary<string, string>() {
504 {"Sarah", "212 555 5555"} // Add some entries to the phone book
505 };
506
507 // Calling SETDEFAULT defined as a generic above
508 Console.WriteLine(SetDefault<string,string>(phonebook, "Shaun", "No Phone")); // No Phone
509 // nb, you don't need to specify the TKey and TValue since they can be
510 // derived implicitly
511 Console.WriteLine(SetDefault(phonebook, "Sarah", "No Phone")); // 212 555 5555
512
513 // LAMBDA EXPRESSIONS - allow you to write code in line
514 Func<int, int> square = (x) => x * x; // Last T item is the return value
515 Console.WriteLine(square(3)); // 9
516
517 // ERROR HANDLING - coping with an uncertain world
518 try
519 {
520 var funBike = PennyFarthing.CreateWithGears(6);
521
522 // will no longer execute because CreateWithGears throws an exception
523 string some = "";
524 if (true) some = null;
525 some.ToLower(); // throws a NullReferenceException
526 }
527 catch (NotSupportedException)
528 {
529 Console.WriteLine("Not so much fun now!");
530 }
531 catch (Exception ex) // catch all other exceptions
532 {
533 throw new ApplicationException("It hit the fan", ex);
534 // throw; // A rethrow that preserves the callstack
535 }
536 // catch { } // catch-all without capturing the Exception
537 finally
538 {
539 // executes after try or catch
540 }
541
542 // DISPOSABLE RESOURCES MANAGEMENT - let you handle unmanaged resources easily.
543 // Most of objects that access unmanaged resources (file handle, device contexts, etc.)
544 // implement the IDisposable interface. The using statement takes care of
545 // cleaning those IDisposable objects for you.
546 using (StreamWriter writer = new StreamWriter("log.txt"))
547 {
548 writer.WriteLine("Nothing suspicious here");
549 // At the end of scope, resources will be released.
550 // Even if an exception is thrown.
551 }
552
553 // PARALLEL FRAMEWORK
554 // https://devblogs.microsoft.com/csharpfaq/parallel-programming-in-net-framework-4-getting-started/
555
556 var words = new List<string> {"dog", "cat", "horse", "pony"};
557
558 Parallel.ForEach(words,
559 new ParallelOptions() { MaxDegreeOfParallelism = 4 },
560 word =>
561 {
562 Console.WriteLine(word);
563 }
564 );
565
566 // Running this will produce different outputs
567 // since each thread finishes at different times.
568 // Some example outputs are:
569 // cat dog horse pony
570 // dog horse pony cat
571
572 // DYNAMIC OBJECTS (great for working with other languages)
573 dynamic student = new ExpandoObject();
574 student.FirstName = "First Name"; // No need to define class first!
575
576 // You can even add methods (returns a string, and takes in a string)
577 student.Introduce = new Func<string, string>(
578 (introduceTo) => string.Format("Hey {0}, this is {1}", student.FirstName, introduceTo));
579 Console.WriteLine(student.Introduce("Beth"));
580
581 // IQUERYABLE<T> - almost all collections implement this, which gives you a lot of
582 // very useful Map / Filter / Reduce style methods
583 var bikes = new List<Bicycle>();
584 bikes.Sort(); // Sorts the array
585 bikes.Sort((b1, b2) => b1.Wheels.CompareTo(b2.Wheels)); // Sorts based on wheels
586 var result = bikes
587 .Where(b => b.Wheels > 3) // Filters - chainable (returns IQueryable of previous type)
588 .Where(b => b.IsBroken && b.HasTassles)
589 .Select(b => b.ToString()); // Map - we only this selects, so result is a IQueryable<string>
590
591 var sum = bikes.Sum(b => b.Wheels); // Reduce - sums all the wheels in the collection
592
593 // Create a list of IMPLICIT objects based on some parameters of the bike
594 var bikeSummaries = bikes.Select(b=>new { Name = b.Name, IsAwesome = !b.IsBroken && b.HasTassles });
595 // Hard to show here, but you get type ahead completion since the compiler can implicitly work
596 // out the types above!
597 foreach (var bikeSummary in bikeSummaries.Where(b => b.IsAwesome))
598 Console.WriteLine(bikeSummary.Name);
599
600 // ASPARALLEL
601 // And this is where things get wicked - combine linq and parallel operations
602 var threeWheelers = bikes.AsParallel().Where(b => b.Wheels == 3).Select(b => b.Name);
603 // this will happen in parallel! Threads will automagically be spun up and the
604 // results divvied amongst them! Amazing for large datasets when you have lots of
605 // cores
606
607 // LINQ - maps a store to IQueryable<T> objects, with delayed execution
608 // e.g. LinqToSql - maps to a database, LinqToXml maps to an xml document
609 var db = new BikeRepository();
610
611 // execution is delayed, which is great when querying a database
612 var filter = db.Bikes.Where(b => b.HasTassles); // no query run
613 if (42 > 6) // You can keep adding filters, even conditionally - great for "advanced search" functionality
614 filter = filter.Where(b => b.IsBroken); // no query run
615
616 var query = filter
617 .OrderBy(b => b.Wheels)
618 .ThenBy(b => b.Name)
619 .Select(b => b.Name); // still no query run
620
621 // Now the query runs, but opens a reader, so only populates as you iterate through
622 foreach (string bike in query)
623 Console.WriteLine(result);
624
625
626
627 }
628
629 } // End LearnCSharp class
630
631 // You can include other classes in a .cs file
632
633 public static class Extensions
634 {
635 // EXTENSION METHODS
636 public static void Print(this object obj)
637 {
638 Console.WriteLine(obj.ToString());
639 }
640 }
641
642
643 // DELEGATES AND EVENTS
644 public class DelegateTest
645 {
646 public static int count = 0;
647 public static int Increment()
648 {
649 // increment count then return it
650 return ++count;
651 }
652
653 // A delegate is a reference to a method.
654 // To reference the Increment method,
655 // first declare a delegate with the same signature,
656 // i.e. takes no arguments and returns an int
657 public delegate int IncrementDelegate();
658
659 // An event can also be used to trigger delegates
660 // Create an event with the delegate type
661 public static event IncrementDelegate MyEvent;
662
663 static void Main(string[] args)
664 {
665 // Refer to the Increment method by instantiating the delegate
666 // and passing the method itself in as an argument
667 IncrementDelegate inc = new IncrementDelegate(Increment);
668 Console.WriteLine(inc()); // => 1
669
670 // Delegates can be composed with the + operator
671 IncrementDelegate composedInc = inc;
672 composedInc += inc;
673 composedInc += inc;
674
675 // composedInc will run Increment 3 times
676 Console.WriteLine(composedInc()); // => 4
677
678
679 // Subscribe to the event with the delegate
680 MyEvent += new IncrementDelegate(Increment);
681 MyEvent += new IncrementDelegate(Increment);
682
683 // Trigger the event
684 // ie. run all delegates subscribed to this event
685 Console.WriteLine(MyEvent()); // => 6
686 }
687 }
688
689
690 // Class Declaration Syntax:
691 // <public/private/protected/internal> class <class name>{
692 // //data fields, constructors, functions all inside.
693 // //functions are called as methods in Java.
694 // }
695
696 public class Bicycle
697 {
698 // Bicycle's Fields/Variables
699 public int Cadence // Public: Can be accessed from anywhere
700 {
701 get // get - define a method to retrieve the property
702 {
703 return _cadence;
704 }
705 set // set - define a method to set a property
706 {
707 _cadence = value; // Value is the value passed in to the setter
708 }
709 }
710 private int _cadence;
711
712 protected virtual int Gear // Protected: Accessible from the class and subclasses
713 {
714 get; // creates an auto property so you don't need a member field
715 set;
716 }
717
718 internal int Wheels // Internal: Accessible from within the assembly
719 {
720 get;
721 private set; // You can set modifiers on the get/set methods
722 }
723
724 int _speed; // Everything is private by default: Only accessible from within this class.
725 // can also use keyword private
726 public string Name { get; set; }
727
728 // Properties also have a special syntax for when you want a readonly property
729 // that simply returns the result of an expression
730 public string LongName => Name + " " + _speed + " speed";
731
732 // Enum is a value type that consists of a set of named constants
733 // It is really just mapping a name to a value (an int, unless specified otherwise).
734 // The approved types for an enum are byte, sbyte, short, ushort, int, uint, long, or ulong.
735 // An enum can't contain the same value twice.
736 public enum BikeBrand
737 {
738 AIST,
739 BMC,
740 Electra = 42, //you can explicitly set a value to a name
741 Gitane // 43
742 }
743 // We defined this type inside a Bicycle class, so it is a nested type
744 // Code outside of this class should reference this type as Bicycle.BikeBrand
745
746 public BikeBrand Brand; // After declaring an enum type, we can declare the field of this type
747
748 // Decorate an enum with the FlagsAttribute to indicate that multiple values can be switched on
749 // Any class derived from Attribute can be used to decorate types, methods, parameters etc
750 // Bitwise operators & and | can be used to perform and/or operations
751
752 [Flags]
753 public enum BikeAccessories
754 {
755 None = 0,
756 Bell = 1,
757 MudGuards = 2, // need to set the values manually!
758 Racks = 4,
759 Lights = 8,
760 FullPackage = Bell | MudGuards | Racks | Lights
761 }
762
763 // Usage: aBike.Accessories.HasFlag(Bicycle.BikeAccessories.Bell)
764 // Before .NET 4: (aBike.Accessories & Bicycle.BikeAccessories.Bell) == Bicycle.BikeAccessories.Bell
765 public BikeAccessories Accessories { get; set; }
766
767 // Static members belong to the type itself rather than specific object.
768 // You can access them without a reference to any object:
769 // Console.WriteLine("Bicycles created: " + Bicycle.bicyclesCreated);
770 public static int BicyclesCreated { get; set; }
771
772 // readonly values are set at run time
773 // they can only be assigned upon declaration or in a constructor
774 readonly bool _hasCardsInSpokes = false; // read-only private
775
776 // Constructors are a way of creating classes
777 // This is a default constructor
778 public Bicycle()
779 {
780 this.Gear = 1; // you can access members of the object with the keyword this
781 Cadence = 50; // but you don't always need it
782 _speed = 5;
783 Name = "Bontrager";
784 Brand = BikeBrand.AIST;
785 BicyclesCreated++;
786 }
787
788 // This is a specified constructor (it contains arguments)
789 public Bicycle(int startCadence, int startSpeed, int startGear,
790 string name, bool hasCardsInSpokes, BikeBrand brand)
791 : base() // calls base first
792 {
793 Gear = startGear;
794 Cadence = startCadence;
795 _speed = startSpeed;
796 Name = name;
797 _hasCardsInSpokes = hasCardsInSpokes;
798 Brand = brand;
799 }
800
801 // Constructors can be chained
802 public Bicycle(int startCadence, int startSpeed, BikeBrand brand) :
803 this(startCadence, startSpeed, 0, "big wheels", true, brand)
804 {
805 }
806
807 // Function Syntax:
808 // <public/private/protected> <return type> <function name>(<args>)
809
810 // classes can implement getters and setters for their fields
811 // or they can implement properties (this is the preferred way in C#)
812
813 // Method parameters can have default values.
814 // In this case, methods can be called with these parameters omitted
815 public void SpeedUp(int increment = 1)
816 {
817 _speed += increment;
818 }
819
820 public void SlowDown(int decrement = 1)
821 {
822 _speed -= decrement;
823 }
824
825 // properties get/set values
826 // when only data needs to be accessed, consider using properties.
827 // properties may have either get or set, or both
828 private bool _hasTassles; // private variable
829 public bool HasTassles // public accessor
830 {
831 get { return _hasTassles; }
832 set { _hasTassles = value; }
833 }
834
835 // You can also define an automatic property in one line
836 // this syntax will create a backing field automatically.
837 // You can set an access modifier on either the getter or the setter (or both)
838 // to restrict its access:
839 public bool IsBroken { get; private set; }
840
841 // Properties can be auto-implemented
842 public int FrameSize
843 {
844 get;
845 // you are able to specify access modifiers for either get or set
846 // this means only Bicycle class can call set on Framesize
847 private set;
848 }
849
850 // It's also possible to define custom Indexers on objects.
851 // Although this is not entirely useful in this example, you
852 // could do bicycle[0] which returns "chris" to get the first passenger or
853 // bicycle[1] = "lisa" to set the passenger. (of this apparent quattrocycle)
854 private string[] passengers = { "chris", "phil", "darren", "regina" };
855
856 public string this[int i]
857 {
858 get {
859 return passengers[i];
860 }
861
862 set {
863 passengers[i] = value;
864 }
865 }
866
867 // Method to display the attribute values of this Object.
868 public virtual string Info()
869 {
870 return "Gear: " + Gear +
871 " Cadence: " + Cadence +
872 " Speed: " + _speed +
873 " Name: " + Name +
874 " Cards in Spokes: " + (_hasCardsInSpokes ? "yes" : "no") +
875 "\n------------------------------\n"
876 ;
877 }
878
879 // Methods can also be static. It can be useful for helper methods
880 public static bool DidWeCreateEnoughBicycles()
881 {
882 // Within a static method, we only can reference static class members
883 return BicyclesCreated > 9000;
884 } // If your class only needs static members, consider marking the class itself as static.
885
886
887 } // end class Bicycle
888
889 // PennyFarthing is a subclass of Bicycle
890 class PennyFarthing : Bicycle
891 {
892 // (Penny Farthings are those bicycles with the big front wheel.
893 // They have no gears.)
894
895 // calling parent constructor
896 public PennyFarthing(int startCadence, int startSpeed) :
897 base(startCadence, startSpeed, 0, "PennyFarthing", true, BikeBrand.Electra)
898 {
899 }
900
901 protected override int Gear
902 {
903 get
904 {
905 return 0;
906 }
907 set
908 {
909 throw new InvalidOperationException("You can't change gears on a PennyFarthing");
910 }
911 }
912
913 public static PennyFarthing CreateWithGears(int gears)
914 {
915 var penny = new PennyFarthing(1, 1);
916 penny.Gear = gears; // Oops, can't do this!
917 return penny;
918 }
919
920 public override string Info()
921 {
922 string result = "PennyFarthing bicycle ";
923 result += base.ToString(); // Calling the base version of the method
924 return result;
925 }
926 }
927
928 // Interfaces only contain signatures of the members, without the implementation.
929 interface IJumpable
930 {
931 void Jump(int meters); // all interface members are implicitly public
932 }
933
934 interface IBreakable
935 {
936 bool Broken { get; } // interfaces can contain properties as well as methods & events
937 }
938
939 // Classes can inherit only one other class, but can implement any amount of interfaces,
940 // however the base class name must be the first in the list and all interfaces follow
941 class MountainBike : Bicycle, IJumpable, IBreakable
942 {
943 int damage = 0;
944
945 public void Jump(int meters)
946 {
947 damage += meters;
948 }
949
950 public bool Broken
951 {
952 get
953 {
954 return damage > 100;
955 }
956 }
957 }
958
959 /// <summary>
960 /// Used to connect to DB for LinqToSql example.
961 /// EntityFramework Code First is awesome (similar to Ruby's ActiveRecord, but bidirectional)
962 /// https://docs.microsoft.com/ef/ef6/modeling/code-first/workflows/new-database
963 /// </summary>
964 public class BikeRepository : DbContext
965 {
966 public BikeRepository()
967 : base()
968 {
969 }
970
971 public DbSet<Bicycle> Bikes { get; set; }
972 }
973
974 // Classes can be split across multiple .cs files
975 // A1.cs
976 public partial class A
977 {
978 public static void A1()
979 {
980 Console.WriteLine("Method A1 in class A");
981 }
982 }
983
984 // A2.cs
985 public partial class A
986 {
987 public static void A2()
988 {
989 Console.WriteLine("Method A2 in class A");
990 }
991 }
992
993 // Program using the partial class "A"
994 public class Program
995 {
996 static void Main()
997 {
998 A.A1();
999 A.A2();
1000 }
1001 }
1002
1003 // String interpolation by prefixing the string with $
1004 // and wrapping the expression you want to interpolate with { braces }
1005 // You can also combine both interpolated and verbatim strings with $@
1006 public class Rectangle
1007 {
1008 public int Length { get; set; }
1009 public int Width { get; set; }
1010 }
1011
1012 class Program
1013 {
1014 static void Main(string[] args)
1015 {
1016 Rectangle rect = new Rectangle { Length = 5, Width = 3 };
1017 Console.WriteLine($"The length is {rect.Length} and the width is {rect.Width}");
1018
1019 string username = "User";
1020 Console.WriteLine($@"C:\Users\{username}\Desktop");
1021 }
1022 }
1023
1024 // New C# 6 features
1025 class GlassBall : IJumpable, IBreakable
1026 {
1027 // Autoproperty initializers
1028 public int Damage { get; private set; } = 0;
1029
1030 // Autoproperty initializers on getter-only properties
1031 public string Name { get; } = "Glass ball";
1032
1033 // Getter-only autoproperty that is initialized in constructor
1034 public string GenieName { get; }
1035
1036 public GlassBall(string genieName = null)
1037 {
1038 GenieName = genieName;
1039 }
1040
1041 public void Jump(int meters)
1042 {
1043 if (meters < 0)
1044 // New nameof() expression; compiler will check that the identifier exists
1045 // nameof(x) == "x"
1046 // Prevents e.g. parameter names changing but not updated in error messages
1047 throw new ArgumentException("Cannot jump negative amount!", nameof(meters));
1048
1049 Damage += meters;
1050 }
1051
1052 // Expression-bodied properties ...
1053 public bool Broken
1054 => Damage > 100;
1055
1056 // ... and methods
1057 public override string ToString()
1058 // Interpolated string
1059 => $"{Name}. Damage taken: {Damage}";
1060
1061 public string SummonGenie()
1062 // Null-conditional operators
1063 // x?.y will return null immediately if x is null; y is not evaluated
1064 => GenieName?.ToUpper();
1065 }
1066
1067 static class MagicService
1068 {
1069 private static bool LogException(Exception ex)
1070 {
1071 // log exception somewhere
1072 return false;
1073 }
1074
1075 public static bool CastSpell(string spell)
1076 {
1077 try
1078 {
1079 // Pretend we call API here
1080 throw new MagicServiceException("Spell failed", 42);
1081
1082 // Spell succeeded
1083 return true;
1084 }
1085 // Only catch if Code is 42 i.e. spell failed
1086 catch(MagicServiceException ex) when (ex.Code == 42)
1087 {
1088 // Spell failed
1089 return false;
1090 }
1091 // Other exceptions, or MagicServiceException where Code is not 42
1092 catch(Exception ex) when (LogException(ex))
1093 {
1094 // Execution never reaches this block
1095 // The stack is not unwound
1096 }
1097 return false;
1098 // Note that catching a MagicServiceException and rethrowing if Code
1099 // is not 42 or 117 is different, as then the final catch-all block
1100 // will not catch the rethrown exception
1101 }
1102 }
1103
1104 public class MagicServiceException : Exception
1105 {
1106 public int Code { get; }
1107
1108 public MagicServiceException(string message, int code) : base(message)
1109 {
1110 Code = code;
1111 }
1112 }
1113
1114 public static class PragmaWarning {
1115 // Obsolete attribute
1116 [Obsolete("Use NewMethod instead", false)]
1117 public static void ObsoleteMethod()
1118 {
1119 // obsolete code
1120 }
1121
1122 public static void NewMethod()
1123 {
1124 // new code
1125 }
1126
1127 public static void Main()
1128 {
1129 ObsoleteMethod(); // CS0618: 'ObsoleteMethod is obsolete: Use NewMethod instead'
1130#pragma warning disable CS0618
1131 ObsoleteMethod(); // no warning
1132#pragma warning restore CS0618
1133 ObsoleteMethod(); // CS0618: 'ObsoleteMethod is obsolete: Use NewMethod instead'
1134 }
1135 }
1136} // End Namespace
1137
1138using System;
1139// C# 6, static using
1140using static System.Math;
1141
1142namespace Learning.More.CSharp
1143{
1144 class StaticUsing
1145 {
1146 static void Main()
1147 {
1148 // Without a static using statement..
1149 Console.WriteLine("The square root of 4 is {}.", Math.Sqrt(4));
1150 // With one
1151 Console.WriteLine("The square root of 4 is {}.", Sqrt(4));
1152 }
1153 }
1154}
1155
1156// New C# 7 Feature
1157// Install Microsoft.Net.Compilers Latest from Nuget
1158// Install System.ValueTuple Latest from Nuget
1159using System;
1160namespace Csharp7
1161{
1162 // TUPLES, DECONSTRUCTION AND DISCARDS
1163 class TuplesTest
1164 {
1165 public (string, string) GetName()
1166 {
1167 // Fields in tuples are by default named Item1, Item2...
1168 var names1 = ("Peter", "Parker");
1169 Console.WriteLine(names1.Item2); // => Parker
1170
1171 // Fields can instead be explicitly named
1172 // Type 1 Declaration
1173 (string FirstName, string LastName) names2 = ("Peter", "Parker");
1174
1175 // Type 2 Declaration
1176 var names3 = (First:"Peter", Last:"Parker");
1177
1178 Console.WriteLine(names2.FirstName); // => Peter
1179 Console.WriteLine(names3.Last); // => Parker
1180
1181 return names3;
1182 }
1183
1184 public string GetLastName() {
1185 var fullName = GetName();
1186
1187 // Tuples can be deconstructed
1188 (string firstName, string lastName) = fullName;
1189
1190 // Fields in a deconstructed tuple can be discarded by using _
1191 var (_, last) = fullName;
1192 return last;
1193 }
1194
1195 // Any type can be deconstructed in the same way by
1196 // specifying a Deconstruct method
1197 public int randomNumber = 4;
1198 public int anotherRandomNumber = 10;
1199
1200 public void Deconstruct(out int randomNumber, out int anotherRandomNumber)
1201 {
1202 randomNumber = this.randomNumber;
1203 anotherRandomNumber = this.anotherRandomNumber;
1204 }
1205
1206 static void Main(string[] args)
1207 {
1208 var tt = new TuplesTest();
1209 (int num1, int num2) = tt;
1210 Console.WriteLine($"num1: {num1}, num2: {num2}"); // => num1: 4, num2: 10
1211
1212 Console.WriteLine(tt.GetLastName());
1213 }
1214 }
1215
1216 // PATTERN MATCHING
1217 class PatternMatchingTest
1218 {
1219 public static (string, int)? CreateLogMessage(object data)
1220 {
1221 switch(data)
1222 {
1223 // Additional filtering using when
1224 case System.Net.Http.HttpRequestException h when h.Message.Contains("404"):
1225 return (h.Message, 404);
1226 case System.Net.Http.HttpRequestException h when h.Message.Contains("400"):
1227 return (h.Message, 400);
1228 case Exception e:
1229 return (e.Message, 500);
1230 case string s:
1231 return (s, s.Contains("Error") ? 500 : 200);
1232 case null:
1233 return null;
1234 default:
1235 return (data.ToString(), 500);
1236 }
1237 }
1238 }
1239
1240 // REFERENCE LOCALS
1241 // Allow you to return a reference to an object instead of just its value
1242 class RefLocalsTest
1243 {
1244 // note ref in return
1245 public static ref string FindItem(string[] arr, string el)
1246 {
1247 for(int i=0; i<arr.Length; i++)
1248 {
1249 if(arr[i] == el) {
1250 // return the reference
1251 return ref arr[i];
1252 }
1253 }
1254 throw new Exception("Item not found");
1255 }
1256
1257 public static void SomeMethod()
1258 {
1259 string[] arr = {"this", "is", "an", "array"};
1260
1261 // note refs everywhere
1262 ref string item = ref FindItem(arr, "array");
1263 item = "apple";
1264 Console.WriteLine(arr[3]); // => apple
1265 }
1266 }
1267
1268 // LOCAL FUNCTIONS
1269 class LocalFunctionTest
1270 {
1271 private static int _id = 0;
1272 public int id;
1273 public LocalFunctionTest()
1274 {
1275 id = generateId();
1276
1277 // This local function can only be accessed in this scope
1278 int generateId()
1279 {
1280 return _id++;
1281 }
1282 }
1283
1284 public static void AnotherMethod()
1285 {
1286 var lf1 = new LocalFunctionTest();
1287 var lf2 = new LocalFunctionTest();
1288 Console.WriteLine($"{lf1.id}, {lf2.id}"); // => 0, 1
1289
1290 int id = generateId();
1291 // error CS0103: The name 'generateId' does not exist in the current context
1292 }
1293 }
1294}
Topics Not Covered ¶
✨ New, 👍 Old, 🎈 LTS, 🔥 Cross-platform, 🎁 Windows-only
-
Attributes
-
Asynchronous Programming
-
Web Development
- ASP.NET Core ✨
-
Desktop Development
- Windows Presentation Foundation 👍 🎈 🎁
- Universal Windows Platform ✨ 🎁
- Uno Platform 🔥 ✨
- WinForms 👍 🎈 🎁
- Avalonia 🔥 ✨
- WinUI ✨ 🎁
-
Cross-platform Development
- Xamarin.Forms 👍
- MAUI ✨