· 6 years ago · Mar 08, 2019, 02:48 AM
1Hello and welcome to the monster guide to all things C#! This thread aims to teach you the fundamentals of programming and give you some practice. There are also some more advanced concepts covered here as well. We will start with what's known as a console application (think command prompt on Windows).
2
3This guide assumes no previous knowledge, but if you're not a beginner and feel I've missed or gotten something incorrect then please let me know. Ready to get started?
4
5Table of Contents
6Introduction
7What is C# and .NET?
8The IDE
9Creating a Project
10Getting Started
11Hello World!
12
13Fundamentals
14Data Types
15Variables
16Converting
17Naming
18Operators
19Commentary
20Syntax Highlighting
21
22Beginner
23Boolean Logic
24Conditionals
25Switches
26Iteration
27Collections
28Methods
29
30Intermediate
31Globals
32Classes
33Enumerations
34Scope
35Properties
36Ternaries
37Parameters
38
39Advanced
40Parameters II (Out)
41String Interpolation
42Inheritance
43Overloads
44Overrides
45Collections II (Generics)
46File Input/Output
47Pointers
48PInvoke
49Threading
50Introduction
51What is C# and .NET?
52It's important we take some time to learn about the language itself, we are going to be using it after all. The C# syntax (format) was modeled around the C family, but took some inspiration from Java to give you the best of both worlds.
53
54C# as a language isn't just limited to desktop development, you can create many things with the language. For example, Xamarin, a mobile development platform, allows you to write your apps in C# and deploy them across all 3 mobile platforms. There's also a powerful engine named Unity which can create impressive games.
55
56We will be working with the .NET framework, one of Microsoft's best creations developed over the course of around 14 years. In a nutshell, it's a nutshell of programming libraries which seriously accelerates the process of developing software. In other languages such as C or C++, you must handle considerably more elements of your software, such as memory management and writing basic things such as outputting a file yourself.
57
58The .NET framework aims to make life that bit easier by sacrificing trace levels of performance for overall quality of life, and it's well worth it. The world appears to agree as .NET employment - specifically C# - is on the rise.
59
60The IDE
61'IDE' stands for Integrated Development Environment. Think of it as software that creates software. A basic IDE will include a code editor for writing your code, a compiler to translate it into (almost) machine-readable code in .NET's case, and a debugger for your application.
62
63Our IDE of choice is Visual Studio which is potentially the best IDE ever created. It's packed with features to make your life as a programmer that bit easier, and it's such a pleasant environment to work in. There are tools such as IntelliSense which mean you don't have to write out your code fully, it will make a guess and refine it as you continue writing.
64
65There are two primary editions of Visual Studio: Community and Enterprise. The former is free and as a beginner will more than serve your needs. The latter, as per the name, targets industrial/professional development and is expensive. But if you're a student, it's your lucky day since student licenses are free. I'm using Enterprise for this tutorial.
66
67Throughout your time, you may see people still using VS predecessors such as 2010 edition. These people claim it's more lightweight and kinder to less powerful computing systems, but even Enterprise can be toned down to match your system specs. Don't be fooled.
68
69Installing may take some time, especially if you don't have any of the prerequisites or frameworks already installed. Return when you're ready.
70
71Creating a Project
72When Visual Studio completes, it will look something like this:
73(picture from an other tutorial of mine) (Click to View)
74
75Before we can actually do anything, we need to create a project first so fire right in and press New Project (Ctrl + Shift + N).
76
77You will be presented with a window that looks like this:
78[Image: mHh2ifc.png]
79
80As I mentioned before, we will be starting with console applications, so select the third option and name your project "CSConsole" and create. After it's done, you will be presented with the code editor: (Click to View)
81
82This is what we can see:
83Left- Toolbox
84Holds our controls (buttons, text boxes, etc.) and would be populated if we were working with WinForms, but more on that later so ignore the window for now. It can also hold templates of code we've already written.
85Top- Toolbar
86Contains our comment & bookmark controls, project states and debug shuttle buttons. As of now, all we care about is the big one that says 'Start'.
87Upper Right- Solution Explorer
88Holds all elements of our project. We can access the properties, view resources and navigate between code files here. Again, not necessary as of now.
89Lower Right- Properties
90This property view will allow us to configure aspects of a control at design time. For example, we can set the name or placeholder of a TextBox there.
91Bottom- Output & Errors
92When building or debugging, the output window acts as our own little console. If there is no error tab, press the View tab at the top and select 'Error List'. It displays information for any compile or build errors and their descriptions & locations.
93
94Getting Started
95Now it's time to dissect this code that Visual Studio has created for us. I know it looks daunting, but don't worry. Breaking it down will make it easier.
96
97At the very top is:
98Code
99using System;
100using System.Collections.Generic;
101using System.Linq;
102using System.Text;
103using System.Threading.Tasks;
104
105This set of instructions must be right at the top of our project. It states we wish to use members of each namespace. This is known as the using directive. Although it's not necessary, it saves us having to qualify it each time we access something in it. For example, if I were writing to the console like so:
106Code
107Console.WriteLine("Hello");
108
109The using System statement would brighten up and show us that we've accessed something in it. If we removed it, we would get a context error. It's also possible to access by writing:
110Code
111System.Console.WriteLine("Hello");
112
113But shortening it is the best route if you're going to use a namespace more than once.
114
115Next, we see:
116Code
117namespace CSConsole
118
119A namespace is a holder for multiple objects. This can be classes, structs, enums and whatever else. We just studied the System namespace above, so continuing with this example, it would look like:
120
121Code
122namespace System
123{
124 public class Console
125 {
126 public static void WriteLine(string value)
127 {
128
129 }
130 }
131
132 public class Environment
133 {
134
135 }
136}
137
138Inside System is a class named Console, and it has a method (sub procedure) named WriteLine where we can supply a string for it to output to the console. You can also see there is another class there named Environment which is there to demonstrate namespaces can hold multiple classes.
139
140Don't worry if you don't understand the exact details, what's important is that you understand this hierarchy model and how various elements are grouped together.
141
142Finally, we have this line:
143Code
144static void Main(string[] args)
145
146The declaration of what's called a method. Inside these brackets is where the actual code we write will be run. Ignore static for now, the main parts of this are:
147void
148Specifies that this particular method does not return anything. It's called to run a sequence of instructions and that's it.
149Main
150The name of our method. In this case, it's named 'Main'. This method is known as the entry point of an application, it's the first method that will be called when started.
151(string[] args)
152Inside the parenthesis is the opportunity to pass arguments (args) to the method which will be used somehow when it's called. In this case, our args is a string array, but don't worry about that. Think of when you drag a file over a program's icon and it starts using that file. Windows has supplied the location of that file to the program's start arguments.
153
154You'll notice that between all these declarations are curly braces. Each of these marks the beginning and end of the declaration. So everything inside the namespace braces is part of the namespace. Everything inside the class (which is contained in the namespace) is part of the class. This all applies even with expressions such as conditionals and loops which you'll learn about later. Again, this is the hierarchical chain I'm trying to show you.
155
156Hello World!
157As is tradition, we're going to start simply by writing out "Hello World!" to the console. This practice has been used since the dawn of modern programming to test how complex a language is by simply printing something out and to test compilation.
158
159We've actually already covered this in the namespace section, but once more, this is the line we'll use:
160Code
161Console.WriteLine("Hello World!");
162
163This line is accessing the Console class (part of the System namespace, remember?) and calling the WriteLine void (returns nothing, just carries out a set of instructions). Remember I said between a method is what will be executed? Well we are going to place this line inside the Main method.
164
165Now, press Start and it will build, then run. But hopefully you noticed something... It printed then closed. This is exactly what we want. WriteLine was our only instruction. When there is nothing left to execute and nothing to await, it will pack up and close automatically.
166
167From experience, you should know it's a two way stream since the console accepts input as well as output. So if we await input, it gives us time to read what happened. Therefore, we'll call a method from Console named 'ReadLine'.
168
169Can you guess how that might look? (Click to View)
170
171Place it below the WriteLine, since instructions are effectively read sequentially i.e. line-by-line. Build and run again, you'll see it worked and we can press Enter to terminate.
172Find Reply Quote Report
173Odus
174Odus Away
175[closed@HF:]
176Posts: 2,977
177Threads: 83
178Popularity: 12
179βytes: β 0
180 #2 02-19-2016, 08:22 PM (This post was last modified: 02-21-2016, 12:35 PM by Odus.)
181Fundamentals
182Data Types
183Throughout programming, we will be working with data and need something to hold it with. This is known as a variable, which stores one value and can change, but its name and type remain constant. The type of a variable reflects what kind of data the variable will hold and each type comes with certain features and functions, known as members. Here are the core/primitive data types in C#:
184byte
185Holds an unsigned 8-bit integer
1860..255
187sbyte
188A signed byte, capable of storing negatives
189-128..127
190short
191Signed 16-bit integer
192-32,768..32,767
193ushort
194Unsigned 16-bit integer
1950..65,535
196int
197A signed 32-bit integer, the most commonly used numeric data type.
198-2,147,483,648..2,147,483,647
199uint
200Unsigned 32-bit integer
2010..4,294,967,295
202long
203A signed 64-bit integer, capable of storing larger values
204-9,223,372,036,854,775,808..9,223,372,036,854,775,807
205ulong
206Storing larger values like the 64-bit integer long, but unsigned
2070..18,446,744,073,709,551,615
208float
209Stores 32-bit floating-point values at 7-digit precision
210-3.402823e38..3.402823e38
211double
212As above but this time 64-bit at 15-16-digit precision
213-1.79769313486232e308..1.79769313486232e308
214decimal
215Lower range but higher precision at 128-bit (think monetary)
216-79228162514264337593543950335..79228162514264337593543950335
217char
218Stores one single unicode character
219Can be literal, hex, cast, or unicode
220bool
221A two-state logic variable
222Represents either true or false
223
224There are two others I haven't included as primitive, they are string and object. The first isn't considered fully primitive. Java represents strings as a class and languages like C which is instead an array of type char that's listed above. The object type is a base class which types will inherit from, in some way.
225
226Nevertheless, they are declared as just the same.
227
228You'll also notice the words 'signed' and 'unsigned' littered across our type table. I left this until last in case you figured it out, but signed is able to represent negative values and unsigned isn't.
229
230This is down to very low-level stuff (as in closer to hardware, not in terms of ease). But one bit is usually reserved for the state flag (positive or negative). There's also two's complement storage which you can look up if you feel compelled, but I don't want to bore you or confuse you so we'll move on!
231
232Variables
233Now that you have a map of core variables, it's time to put this into practice. But first, below is a test showing you various values and it's your goal to guess their type.
234"Hello world!"
235-12345678
2361234.56
237"true"
238true
239't'
2403
241Answers (Click to View)
242
243Using these in C# is very simple. This is how they're declared:
244Code
245<type> <name> = <value>;
246int x = 1;
247
248As you can see, it's very concise and easy to understand. The only thing there we haven't covered is the semi colon (;) at the end, which represents the end of the line. Breaking that statement down:
249<type>
250The type of data our variable will hold (int, string, byte, etc.)
251<name>
252The alias for our variable which we will write when referencing. This can be named anything you like, it's named 'x' in the example
253=
254Known as an operator, this specifies that the variable will be initialized with the value on the right
255<value>
256An initial value for the variable, it's optional but recommended. If you don't include this, don't include an assignment operator either
257
258C# is a strong-typed language, meaning that variable types must be explicit. Unlike languages such as Javascript where you can freely twist and shape a variable as you please, something like this wouldn't work:
259Code
260int x = 5;
261x = "hello";
262
263You would see the code editor render it as a type conversion error under the string, underlined with a squiggly red line. That isn't the only reason, certain types have certain members associated with them. For example, strings have a property named Length (myString.Length) which represents the number of characters in the string (remember, string is essentially a collection of type char). Such a property wouldn't be available in a type like an int.
264
265What makes a program is being able to work with data you don't know, but we need to get this data first. The ReadLine() method we called to suspend the console will read the user input into a string. Like so:
266Code
267string input = Console.ReadLine();
268
269We will create a program to welcome a user by his name which we prompt for. To do this, we must:
270Prompt the user to enter their name
271Read their name into memory (a variable)
272Print out their name with a welcome message
273Write out the a line saying "Enter your name:" and then read their input into a variable of type string named "name".
274
275Attempt the the first and second parts yourself, collapse the spoiler if you can't get it. (Click to View)
276
277The next part is to join the welcome string with their name. We will look at constants for this. Constants are similar to variables, but they can only be initialized once, the compiler will not permit an attempt to change it. It looks like this:
278Code
279const string welcomeMsg = "Welcome, ";
280
281Building strings in programming is known as string concatenation. It's done like this:
282Code
283Console.WriteLine(welcomeMsg + name);
284
285Which should produce on the console:
286Quote:
287Welcome, Odus
288
289To get some more practice using variables, we will create a program that calculates the perimeter of a rectangle. Declare an integer named 'width' and call ReadLine() to get the user's input.
290
291You'll get an error. The next chapter goes on to explain why and how to fix it.
292
293Converting
294Remember back when I said C# is a type safe language? Well, when attempting to populate our int 'width', we called ReadLine() but it returns a string of the user's input.
295
296There are two main ways to work between types:
297Convert
298Cast
299
300Converting, as the name suggests, attempts to convert the input into the desired type. Casting assumes the data is already the correct type but it just needs to be realized as one. This is an important difference for down the road.
301
302We can convert our input like so:
303Code
304string input = Console.ReadLine();
305int width = Convert.ToInt32(input);
306
307We're calling the Convert class (from System namespace) and specifying we want an int. Think back to the data table, an int(eger) is a signed 32-bit integer, hence 'Int32'. Think of int as an alias to Int32.
308
309It's also safer in the sense that it raises the opportunity for exceptions. Casting certain types does not. For instance, say we wish to cast an integer into a byte (unsigned 0-255). We could cast like so:
310Code
311int src = 256;
312byte dest = (byte)src;
313
314However, src is out of the byte storage range, our dest will hold 0 (range is offset by 1, more on that later). Converting (Convert.ToByte(src);) would throw, in this case, an overflow exception since the source was out of range.
315
316We will now perform conversions for both width and height. You can reuse the input string to read input again for the height.
317
318Collapse if you're having trouble (Click to View)
319
320You'll notice in there I called 'Write' instead of 'WriteLine'. The difference is that the latter writes the output and terminates the line i.e. carriage return. The former just writes to the console, hence the space. Calling ReadLine() will read and terminate the line. It will look a lot cleaner, try it yourself!
321
322I'm also writing a space at the end so the input isn't squashed next to the instruction.
323
324The next part is to plug these values into the perimeter formula.
325
326Naming
327Since variables can be aliased as we please, it's important that we know how to choose and name our variables in a way that the data they're holding can be easily identified. To keep it brief and so as not to insult your intelligence, give the list below a read.
328Camel cased
329int firstAge; - first word lower, second identified by its title-case
330Easily readable and identifiable.
331int age;
332Don't shorten to the point where it hinders the above.
333int a;
334Avoid association with core keywords/identifiers
335int publicAge;
336Never use Hungarian notation
337int intAge; - prepending the contracted (if necessary) data type
338
339Classes, namespaces and methods should be pascal-cased (class ConsoleApplication). For now, that's essentially it. The casing difference between naming makes such a huge impact on readability, you'll have a hard time reading someone's code which hasn't been written adhering to these guidelines.
340
341Operators
342By definition, an operator is a character that represents an action (operation) on any number of subjects (known as operands). There are many in C#, but we will only look at the core numeric operators in this chapter. Others will be introduced when they come into play.
343
344You'll find these are near identical to real world mathematics. Arithmetic operators include:
345Additive
346+
347Adds one operand to another
348-
349Subtracts one operand from another
350Multiplicative
351*
352Multiplies two operands together
353/
354Divides two operands
355%
356Known as modulus, it computes the remainder from a division between two operands
357Primary
358i++
359Returns the existing value and then increments by 1
360i--
361Returns the existing value and then decrements by 1
362Unary
363++i
364Increments by 1 before returning the (updated) value
365--i
366Decrements by 1 before returning the (updated) value
367Assignment
368=
369Assigns the right operand to the left
370+=
371Increments by the right operand
372-=
373Decrements by the right operand
374*=
375Multiplies by the right operand
376/=
377Divides by the right operand
378%=
379Performs a modulus on the target by the right operand, sets the value as the result
380All but the first of the assignment operators can be considered as contractions of sums. For example:
381Code
382int i = 1;
383i += 1;
384
385Produces exactly the same (i = 2) as:
386Code
387int i = 1;
388i = i + 1;
389
390Which can be shortened by:
391Code
392int i = 1;
393i++;
394
395Back to our rectangle project, the formula to calculate the perimiter (sum of all sides) is: P = 2(l+w), L being length and W being width. This can be represented as (l * 2) + (w * 2) and it's how we'll calculate it in our project.
396
397You'll find that a lot of programming involves plugging values into formulas and it's no different to mathematics to be less alien.
398
399Apply this formula by creating a new variable of type int and name it 'perim'. After writing in the formula, press Start to debug and we will try it out.
400
401My test values of 5 and 3 should produce 16, and it does!
402
403[Image: 5HrQGFb.png]
404
405Your code should look something like this:
406Code
407string input;
408
409Console.Write("Enter the width: ");
410input = Console.ReadLine();
411int width = Convert.ToInt32(input);
412
413Console.Write("Enter the height: ");
414input = Console.ReadLine();
415int height = Convert.ToInt32(input);
416
417int perim = (width * 2) + (height * 2);
418Console.WriteLine("Perimiter: " + perim);
419
420Console.ReadLine();
421
422I've written input as an empty string at the top to demonstrate that primitive type variables don't always require a value on declaration. This changes as we move on to more complex objects, but more about that later.
423
424Commentary
425An integral part of programming is internal commentary. Before I explain, let me show you an example of a comment:
426Code
427//Calculate perimeter
428int perim = (width * 2) + (height * 2);
429
430You see "Calculate perimiter" prefixed with a double forward slash? That's called a comment. They have a variety of uses, including:
431Describing code
432Ignoring code
433Inline developer notes (TODO, BUG, etc.)
434Information for other programmers using your code
435It doesn't matter what you write, from actual code to a Harry Potter book, the compiler will not recognize it as valid instructions.
436
437Although it's good practice to document your code with comments, don't go overboard with it. Things such as:
438Code
439//Read console into input
440input = Console.ReadLine();
441
442Where a block of code's purpose is clear, commentary isn't needed. This stuff is designed to be readable and comments should serve as a boost.
443
444The double slash ("//") is used for single line comments, but what if we need to comment multiple lines? There is a solution for that, multi line comments look like this:
445Code
446/*
447* A line
448* Another line
449* One more line should do it
450*/
451
452The top line ("/*") can also be used to comment, it comes down to preference. The asterisks are also optional. Multi line comments are useful for commenting out blocks of code, for example:
453Code
454/*
455Console.Write("Enter the width: ");
456input = Console.ReadLine();
457int width = Convert.ToInt32(input);
458*/
459
460Will be recognized as comments. You can identify commentary by its solid green color.
461
462Syntax Highlighting
463Speaking of colors in the code editor, it's time we took a look at syntax highlighting. Although this chapter could be grouped in the IDE chapter, explaining its uses and giving examples may have thrown you off. But you've lasted this far, so good job!
464
465You'll have noticed that your code sports a few colors. I'm using the dark theme, so my code looks like this in the editor:
466
467[Image: CNrVvVI.png]
468
469Lets break down these colors.
470blue
471Reserved for core keywords of the language. Things such as primitive types, access modifiers, <1 char operators (typeof for example).
472pale green
473Identifies classes and objects.
474terracotta
475Exclusive for pre-defined (a.k.a hardcoded) strings
476dark green
477Marks commentary, both single and multi line
478pale green
479Numeric values, 1 char operators (+, -, etc.) and enum (you'll learn about these later) name highlighting
480
481These colors will change with your theme or you can install custom coloring packages, but the laws of coloring above will always remain unless you disable syntax highlighting.
482Find Reply Quote Report
483Odus
484Odus Away
485[closed@HF:]
486Posts: 2,977
487Threads: 83
488Popularity: 12
489βytes: β 0
490 #3 02-19-2016, 08:23 PM (This post was last modified: 03-04-2016, 05:49 PM by Odus.)
491Beginner
492Boolean Logic
493There are three main elements to programming: sequence, selection and iteration. We've already looked at sequence (line-by-line), now we'll look at selection. But before we can go straight into it, we need to look at something called boolean logic. If you've done any intermediate circuitry work, this will be easy for you.
494
495As the name suggests, selection involves making choices. So far, all our work has been one direct path from top to bottom. Selection gives us the opportunity to branch into a different code path depending on certain parameters. What these are, is our decision.
496
497Back in the Fundamentals section, we looked at primitive types and one named 'bool' was there, representing true or false. We can compute what are known as expressions in order to give us this boolean logic. Declaring is just the same as before, it looks like this:
498Code
499bool valid = false;
500
501Using this kind of data requires a new set of operators. Like before, a chart is below showing what you'll need to start with.
502Equality
503==
504Reference equality
5051 == 1 = true
506!=
507Reference inequality
5081 != 1 = false
509
510Relational
511<
512Less than
5131 < 2 = true
514>
515Greater than
5161 > 2 = false
517<=
518Less than or equal to
5191 <= 1 = true
520>=
521Greater than or equal to
5221 >= 2 = false
523Conditional
524AND
525&&
526(true && true) = true
527(true && false) = false
528(false && false) = false
529OR
530||
531(true || true) = true
532(true || false) = true
533(false || false) = false
534XOR
535^
536(true ^ true) = false
537(true ^ false) = true
538(false ^ false) = false
539
540XOR (exclusive-or) works a little different than the rest. It must evaluate both operands before it can draw its conclusion, the rest are known as short-circuit operators. If the first will cause the resultant to fail, it will short the block and not bother evaluating any subsequent conditions.
541
542There's also one other operator which is known as negation. It negates the expression/bool and is simply an exclamation point like the reference inequality operator. For example:
543Code
544bool valid = false;
545valid = !valid;
546
547In that sample, our bool is being given the opposite value of itself. It's currently false, so negating that will turn it to true.
548
549In this chapter, we will only study the equality set, the rest will follow in the next chapter. Remember that the reference equality operator is different from the assignment operator (=). It makes distinguishing between them much easier than in other languages which don't support it. For example:
550Code
551//assume 'pin' is of type string
552bool validPIN = pin.Length == 4;
553
554This expression computes the length of the string 'pin' (we mentioned the Length property briefly before) against 4, since PINs are generally 4 digits in length.
555
556If we entered "1234", our bool named 'validPIN' would turn true. If we entered anything below or above 4, it would return false. We could be more diverse in our length checks, how about we wanted to validate a minimum length? Also simple enough with our relational operators. Like so:
557Code
558bool validPass = password.Length > 7
559
560This will require that the password length is at least 8 (greater than 7) characters in length. Equally, that same expression can also be represented as:
561Code
562bool validPass = password.Lengh >= 8
563
564In situations like this, it comes down to preference, but each have their own uses which you'll find out later.
565
566Conditionals
567In the previous chapter, I mentioned that selection was a part of programming. Boolean logic was first, this is the second half and we start with what's known as an If statement. These can be used to make a selection based on logic that we studied above, and the program can branch depending on the result. We'll take a look at this by creating a program informing the user if they are or above the legal driving age.
568
569The first part is up to you. Create a constant int with your country's driving age. For example, it's 16 for a learner's permit in the USA so your constant's value will be 16. You then want to ask the user if they are over this age, but don't read the input just yet.
570
571If you're having trouble or want to check, the solution is here. (Click to View)
572
573Keep in mind the naming conventions we spoke about earlier. Your programs should be highly maintainable, meaning it's quick and easy to update without breaking too much (if anything). This is why we've opted for a constant.
574
575This is how we could read the input:
576Code
577char answer = (char)Console.Read();
578
579We're opting to read the answer as Y(es) or N(o). You've seen Write and WriteLine, the same applies for the Read set. Calling Read() on its own reads only the first character from the stream. Because of this, we can safely cast instead of having to call from the Convert class like we did earlier.
580
581However, it leaves after reading the first character, leaving the rest of the line awaiting consumption, which ReadLine does, leaving no further instructions and terminates like we spoke about right at the beginning. An easy fix is to call 2x ReadLines, but we'll instead switch to ReadKey, which looks like this:
582
583Code
584char answer = Console.ReadKey().KeyChar;
585
586Calling ReadKey alone returns a reference type ConsoleKeyInfo which allows access to various attributes of the console key such as modifiers. We only want the actual Unicode character that was pressed, so we must read from its KeyChar property as you can see from above.
587
588But we're not out of the woods yet. This call is made the moment a key is pressed and we want the user to submit it themselves by pressing Enter. Calling ReadKey doesn't terminate the line either, so to kill 2 birds with 1 stone, we'll call ReadLine just below it.
589
590If you're not feeling confident, skip this paragraph and move straight to the next paragraph, else keep reading. If you run this, you'll see that the first key is now part of the output stream and cannot be undone. We can resolve this by going back to our old friend, ReadLine. Since it returns a string and strings are essentially an array of chars, we can read from index, meaning we can read each character of the string individually. After calling ReadLine, append "[0]" to the end, but before the semi-colon (line terminator). This will read the first position since collections start at 0 in programming, but more on that in the Arrays chapter.
591
592Now it's time to evaluate this char using an If statement, which looks like this:
593Code
594if (<condition>)
595{
596 <true instructions>
597}
598else
599{
600 <false instructions>
601}
602
603The first part, <condition> must be a logical expression, meaning that it returns a bool. If this condition evaluates to true, everything inside the true instructions braces will be executed, if not, everything in the false instructions braces will be executed. These braces follow the same principle as braces in namespaces, classes and methods.
604
605The else block is optional.
606
607To put this into practice, we'll use the reference equality operator (==) against the char:
608Code
609if (answer == 'y')
610{
611 Console.WriteLine("You are old enough to drive.");
612}
613
614However, this will only fire if the input is 'y', reading it as 'Y' wouldn't satisfy the condition. We could make it lower case by default, but this is the perfect time to introduce conditional operators.
615
616Conditional operators (AND [&&], OR [||], XOR [^]) allow us to combine multiple expressions into one final condition, which will be used to decide the path. Since we wish to compare both cases, we'll select the OR operator for this task. Like so:
617
618Code
619if (answer == 'y' || answer == 'Y')
620{
621 Console.WriteLine("You are old enough to drive.");
622}
623
624Now we need to account for N submissions. Although introducing an else block would do the trick, it would also accept any other character as a contender for not N. This is fixable through use of elseif. Which looks like:
625Code
626else if(answer == 'n' || answer == 'N')
627{
628 Console.WriteLine("You aren't old enough to drive yet.");
629}
630
631And is added directly below the if statement. Should the original if fail, flow will move to the else-if block and evaluate. So at this point, your code should look like this:
632
633Code
634const int legal_age = 16;
635
636Console.Write("Are you over " + legal_age + "? (Y/N)");
637char answer = Console.ReadKey().KeyChar;
638
639Console.ReadLine();
640
641if (answer == 'y' || answer == 'Y')
642{
643 Console.WriteLine("You are old enough to drive.");
644}
645else if(answer == 'n' || answer == 'N')
646{
647 Console.WriteLine("You aren't old enough to drive yet.");
648}
649
650Console.ReadLine();
651
652There is just one things we haven't accounted for, and that's unaccounted-for chars. Anything other than Y/N is invalid so we should handle that as anything else. You guessed it, it's an else statement, which looks like this:
653Code
654else
655{
656
657}
658
659As you can see, there's no condition that must be met, code will only fall to this block if no of the previous conditions are met and this is what we're looking for. It works identically to if/elseif, so place a WriteLine call that informs them their input is invalid.
660
661Switches
662In the previous chapter, we examined conditionals through the If statement. Now, we'll take a look at its 'brother', the Switch. Don't worry, it's nothing near as complicated as it may sound. This is how it looks:
663
664Code
665switch (subject)
666{
667 case <value>:
668 <action>
669 break;
670 case <another value>:
671 <another action>
672 break;
673 default:
674 <failed action>
675 break;
676}
677
678Similar to If, it also represents a crossroads for logic as you can see above, but works a bit differently in its use. Unlike If, the Switch is incapable of computing cases as expressions, they are taken at face value. For example, you couldn't write:
679Code
680switch (someInt)
681{
682 case someInf < 5:
683//...
684
685It must be directly comparable, think reference equality operator. The keyword 'break' is bolted to the bottom of each condition action block in order to break from the switch. Failing to include this means it will fall through to the next condition, which is how we can use multiple cases against one action block. It's also there for readability's sake. Remember, C' is modeled from the C family and Java.
686
687The last part of it is 'default' which is identical to 'else' of the If set. Should no case fit, it will default to this block. It's not mandatory to actually do anything other than include a break keyword, but it's good practice and is enforced by some similar languages. Consistency!
688
689If we converted our code to include a Switch rather than an If, it would look like this:
690Code
691switch (answer)
692{
693 case 'Y':
694 case 'y':
695 Console.WriteLine("You are old enough to drive.");
696 break;
697 case 'N':
698 case 'n':
699 Console.WriteLine("You aren't old enough to drive yet.");
700 break;
701 default:
702 Console.WriteLine("You haven't entered a valid answer.");
703 break;
704}
705
706As you can see, stacked case conditions fall through to the action block, similar to the OR (||) operator in conditionals. You may also hear that these can be faster than If statements. The truth is, they can. But not always. It comes down to variable density and depth. It may not even compile to what a switch should compile to (jump table), but a tower of Ifs. So don't immediately favor this against Ifs. Also keep in mind lack of condition testing as cases.
707
708Iteration
709Back in the Boolean Logic chapter, I mentioned there are three main elements to programming: sequence, selection and iteration. We've looked at sequence, covered selection above and it's time to look at iteration.
710
711A loop is a statement, or set of statements, repeated a specified number of times or until some condition is met. The type of loop you use depends on the task at hand, these are the main two types:
712
713For
714While
715
716The first operates using an iterator (index) variable which is increased on each lap of the loop. The second works using an expression which is computed each iteration and control is only transferred outside the loop when it evaluates to false. We use loops in order to avoid repeating ourselves over and over again in terms of code. It also means we can make small changes on each iteration.
717
718Taking a look at the For loop, it's organized like this:
719Code
720for (<iterator> = <initial value>; <condition>; <increment>)
721{
722 <instructions>
723}
724<iterator>
725A loop control variable that is declared for the loop and initialized once
726<initial value>
727Starting value of the above variable
728<condition>
729An expression which determines how long the loop will run, usually involved the iterator variable
730<increment>
731Called after each iteration, usually to update the loop variable
732An actual For loop would look like this:
733Code
734for (int i = 0; i < 5; i++)
735{
736 Console.WriteLine(i);
737}
738
739In flow terms, this is the order in what's happening:
740Loop variable is declared and initialized once
741Condition is evaluated and will execute one iteration if it returns true
742Instructions in the loop block are executed
743Control update/increment takes place (i++, remember incrementing by 1?)
744Steps 2-4 are repeated until the evaluation returns false. Running this, the console will print:
745Quote:
7460
7471
7482
7493
7504
751
752It's iterating 5 times, but when control variable i is incremented again and returns as greater than 5, the loop is exited. Our iterator has started from 0 and only gone up to 4. It's important you note that is still 5 iterations, this is crucial for the next chapter.
753
754Moving on to the While loop, it looks like this:
755Code
756while (<condition>)
757{
758 <instructions>
759}
760
761Unlike the For loop, this one keeps track of no iteration and it's possible to go on forever (what's known as an infinite loop) easier than its counterpart. It will run so long as the condition evaluates to true.
762
763As an example, we'll convert our For loop into a While loop:
764Code
765int i = 0;
766
767while (i < 5)
768{
769 Console.WriteLine(i);
770
771 i++;
772}
773
774It will print exactly the same thing (0-4 each on a new line), only we're incrementing ourselves. Removing 'i++;' would continuously print "0", an infinite loop since variable 'i' will never increment, so it will never exceed 5, therefore no opportunity to break.
775
776A difference between these is that variable 'i' is accessible outside the While loop, we can continue to use it and do whatever we want with it. It's value will be 5 should we read from it after the loop. For, however, limits this variable only to what's inside the loop. This concept is known as "scope", meaning at what level something can be accessed at, and it applies to everything. If statements, loops, switches, methods, classes (sort of), etc.
777
778We can demonstrate this loop using our driving age calculation, looping until valid input has been made. Create a variable of type bool and name it as 'validInput', initialize it as false. Then create a while loop just below asking the question and above reading the input. Test the negated value (! operator) of this validInput bool. Surround everything before the ReadLine stall in braces as it's in the scope of the loop.
779
780Before the break keyword of Y and N cases, set validInput to true, so it knows to break from the loop. If you're struggling, collapse this for an example of my code. (Click to View)
781
782We're not bothering to set validInput to false in our default-case instructions since it's already been set and there's no place it could have changed other than inside valid input, in which case the control will transfer outside the loop.
783
784Take it for a spin and you'll notice it will continually prompt for an answer until it gets something valid. Awesome!
785
786The While loop also has a sibling of its own, called the "Do-While". It looks like this:
787Code
788do
789{
790 <instructions>
791} while (<condition>)
792
793The key difference is pre and post conditional checking. I've drawn a diagram below to explain the difference. Control flow for the original While goes like this:
794Evaluate condition
795Execute block if true, exit if false
796Do While, however, goes like so:
797Execute block
798Evaluate condition
799If true, go back to step 1 and execute, if false, exit block
800With the first, condition is evaluated before the first iteration, so what's inside the loop block may never be executed. The second performs one full iteration before performing the check, so at least one lap is guaranteed.
801
802Collections
803Now that we can repeat code with loops under our belt, I want to ask you this: What if we wanted to determine ages for a group of people? Writing something like:
804Code
805int firstAge = 18;
806int secondAge = 27;
807int thirdAge = 15;
808...
809
810Would get very tedious. It also means we're limiting ourselves to a set number. This is where collections come in, we'll be studying the array in this chapter.
811
812Arrays are simply a group of types at a fixed size. We can refer to them by the same alias but supply an index to get a value at a position in the array. It sounds complicated, but let me show you...
813
814Declaring an array looks like this:
815Code
816int[] ages = new int[5];
817
818Here we're declaring a new int by the name of 'ages', but you'll notice two square brackets bolted onto the end of the type. This is how we can identify an array and you've just covered the third and final brace! The first; regular parenthesis, is for methods and containing args, the second; curly braces, are markers for classes and methods and whatnot, square; what you've just seen now, is used for indexing on collections.
819
820Where it's initialized, we see the 'new' keyword, which means we're spawning a new instance of an array of type int. In the square brackets is the number 5, so this array 'ages' has the capacity to store 5 individual values all under a single roof.
821
822We can populate these positions like so:
823Code
824ages[0] = 18;
825ages[1] = 21;
826...
827
828Now you'll notice I started at position 0. Back in the collections chapter, I mentioned that knowing from 0-4 was a total of 5 steps being crucial, and this is why. Arrays are 0-based, meaning they start from 0. The best analogy I can give is to think of a building at the ground floor. You're neutral at the first position. Going negative would mean you're going underground. At 1 would suggest you're 1 level above i.e. on the first floor.
829
830So if we attempted to populate position 5:
831Code
832ages[5] = 50;
833
834It would crash, giving us an out of bounds exception since we're attempting to access a position larger than the array's capacity/size. [4] would be the last position, but it's still 5 steps from 0.
835
836Now this is the fun part where we merge iteration with collections. That For loop supplies indexing and we'll use it to communicate data to our array. Remember how to read an int from the console?
837Code
838int input = Convert.ToInt32(Console.ReadLine());
839
840We're going to use that same line. Scrap your existing code except the final ReadLine method & ages int[] declaration. Create a For loop with a variable named 'i' as the iterator then set its value to 0. For the condition, i should be less than the ages array's length (think .Length property) and we want to increment i by one each iteration.
841
842Solution (Click to View)
843
844We could equally use the condition as i < 5, but comparing to the array's length allows us to change the size of the array in the code without having to alter the array condition as well. This is why we're using the Length property of the array.
845
846To prompt the user, we want to write "Enter age for position ", concatenated with i and followed by a colon (:) & a space for padding. Read the entire line as input to terminate the line for the next iteration.
847
848Can you think on how to access a position in the array? Variable 'i' is an int and represents the current iteration, we use square brackets at the end of the alias to qualify the index.
849
850Your code should look like this:
851[Image: Puju8ok.png]
852
853Good job if you arrived here yourself. At this point, the user's input has been read into an array. We're now going to iterate through once more and process it. Since we're reading ages instead of matching chars, we now have some mathematical work to do. The value at the position of ages[] must be greater than or equal to than our driving age (legal_age constant).
854
855We want to test this using a conditional. If it returns true, print:
856Quote:
857User at position {current position} is old enough to drive.
858
859Else, we print:
860Quote:
861User at position {current position} is not old enough to drive yet.
862
863To achieve this, think on string concatenation (hint: +) and how we access a position's value in an array. This is what you should have:
864[Image: FuilYQz.png]
865
866Now we're going to go one step further. If the user isn't old enough to drive yet, we'll calculate how far off they are from the legal driving age. The formula for this is to subtract the legal_age constant from the user's age. Store this value in a variable of type int named 'difference'. Adjust your console output to this:
867Quote:
868Console.WriteLine("User at position {current position} is not old enough to drive yet. They will be old enough in {difference} years.");
869
870Running this against ages: 10, 21, 17, 16, 12 should output this:
871Quote:
872Enter age for position 0: 10
873Enter age for position 1: 21
874Enter age for position 2: 17
875Enter age for position 3: 16
876Enter age for position 4: 12
877User at position 0 is not old enough to drive yet. They will be old enough in 6 years.
878User at position 1 is old enough to drive.
879User at position 2 is old enough to drive.
880User at position 3 is old enough to drive.
881User at position 4 is not old enough to drive yet. They will be old enough in 4 years.
882
883If not, this is how it should look:
884[Image: gwlcte2.png]
885
886There's just one loop left I've left until last and that's so you'd understand it better after some practice. It's called ForEach and is a version of the For loop. It looks like this:
887Code
888foreach (<type> <variable> in <collection>)
889{
890 <instructions>
891}
892
893You can see the similarities, but this is what's different:
894type
895Type of the iterator (int, string, etc.)
896variable
897Loop variable
898collection
899An array of some sort to iterate through
900
901In this, we don't reference by index. We reference using our iterator variable which progresses to the next value in the collection with each iteration. If we don't need to use indexing anywhere in our loop instructions, we can use this for a cleaner and easier to read loop.
902
903If we switched out our current For loops to ForEach, it would look like this:
904[Image: mtwGv7q.png]
905
906You'll notice that only the second loop was changed. This is because the iterator variable is read only. In most cases, the iterator type will match the type of the collection, though this isn't enforced. We can't update the position's value from it. Since there's no position, I also reworded the console output.
907
908Methods
909Hopefully you've had some practice at the fundamentals and beginner level concepts. It's now time to move on to more intermediate stuff and we're going to dive right in with methods.
910
911Until now, we've only practiced inside Main, the entry point method of our application and that they must be placed in some sort of class. Now we're going to try out working with other methods. There are two types of methods:
912Static
913Instance
914The first is identified by the 'static' keyword which is also seen in Main, the latter isn't. We've already called plenty of static methods such as Console.Read/Write(Line). But there are methods which can only be accessed through an instantiated (instance) object. For example, string has a method named "ToUpperCase" (converts all letters of the string to their upper case format) which can only be accessed on a string.
915
916We will look at something called a sub routine, identifiable by its keyword "void", meaning that it doesn't return anything, which we covered earlier. This is ideal for calling a set of instructions by one name. Similar to a variable, we can call it over and over again but only having to write it out once. It can simplify your code big time. For example, lets look at the Console.WriteLine sub routine.
917
918Code
919public static void WriteLine(string value)
920{
921
922}
923
924Analyzing each keyword:
925public
926At what level it can be accessed at, more of that in the next chapter.
927static
928Static keyword means it's a static method
929void
930Return type. Void returns nothing, but we can also switch this with string, int, bool, etc. to return a value of that type
931WriteLine
932Name of the method. Its function should be clear and concise
933string value
934Parameter type and alias, it's a value we pass to the method that will be used. In WriteLine, this value is written to the console
935Sticking with our previous code, which is this:
936Code
937const int legal_age = 16;
938int[] ages = new int[5];
939
940for (int i = 0; i < ages.Length; i++)
941{
942 Console.Write("Enter age for position " + i + ": ");
943 ages[i] = Convert.ToInt32(Console.ReadLine());
944}
945
946foreach (int age in ages)
947{
948 if (age >= legal_age)
949 {
950 Console.WriteLine("User aged" + age + " is old enough to drive.");
951 }
952 else
953 {
954 int difference = legal_age - age;
955
956 Console.WriteLine("User aged" + age + " is old enough to drive yet. They will be old enough in " + age + " years.");
957 }
958}
959
960Console.ReadLine();
961
962We'll create a new static method with a return type of void and name it "PromptForAge", taking no parameters (empty parenthesis).
963
964Code
965public static void PromptForAge()
966{
967
968}
969
970This is written just below the closing brace of the Main method, it's still inside our Program class, so the access modifier (public) can be safely changed to private, more on that in the next chapter.
971
972In this method, Write (but don't terminate the line) to the console:
973Quote:
974Console.Write("Enter age: ");
975
976Now take out the first line where you're writing this to the console inside the For loop and begin to write "PromptForAge". You'll see IntelliSense, the little window that guesses what you want to write, start to bring up your method. Press enter or tab to complete. You must fill in the parenthesis and terminate the line (semi-colon) yourself.
977
978Run and see that this method is called, printing to the console. Awesome! The only difference is that we can call this method outside the loop, even if we don't need it, it's there anyway.
979
980To give you a more practical example, we'll rewrite our legal age determination into a method with a return type of bool, this kind of method is known as a function and is written exactly the same, only 'void' is switched with the return type. Name it "CanDrive" and accept a parameter of type int and name it "age". Look back at the Main method for passing args.
981
982It should look like this:
983[Image: FN5IYlk.png]
984
985You'll notice the squiggly red line highlighting an error. It reads:
986Quote:
987Program.CanDrive(int)': not all code paths return a value
988
989It's right. We're not returning anything, but don't worry, we'll fix it now. Since we're moving the age calculation into a method, move the legal_age const in with it as we won't need it inside the Main method. Now we want to create a bool which is valued on the age parameter being greater than or equal to the legal_age const, just like in our Main method.
990
991It should look like this:
992[Image: FUDeZ05.png]
993
994It's possible to omit the storage bool and return directly:
995Code
996return age >= legal_age;
997
998But for demonstration, I chose to go the long way. Now, replace the age difference calculation inside the If condition with a call to CanDrive, supplying the age iterator variable from the loop as an argument. Like so:
999
1000[Image: WGmhgzt.png]
1001
1002Now there's one error left:
1003Quote:
1004The name 'legal_age' does not exist in the current context
1005
1006This error is thrown because we have moved it to a method. But we're also going to move the age calculation into its own method too. Create a method named "AgeDifference", accepting an arg of type int named 'age'. We will return the difference between the legal_age const and the parameter sent. Copy the legal_age const into this method as well.
1007
1008[Image: WGHuaUZ.png]
1009
1010Now lets head back to our Main method and remove the variable 'difference'. Replace where we reference this variable in the WriteLine method with a call to AgeDifference, supplying the loop variable 'age' as an argument:
1011Code
1012AgeDifference(age)
1013
1014As you can see, methods can be called anywhere, even inside an operation such as string concatenation. Your else statement should look like this:
1015
1016[Image: 1dHk2iw.png]
1017
1018Since we now have the age calculator in a method, we could even show how many years ago a person over the legal age was able to drive. But this would return a negative number, so you would have to negate it back to positive before displaying. Attempt it if you wish.
1019Find Reply Quote Report
1020Odus
1021Odus Away
1022[closed@HF:]
1023Posts: 2,977
1024Threads: 83
1025Popularity: 12
1026βytes: β 0
1027 #4 02-19-2016, 08:23 PM (This post was last modified: 03-05-2016, 09:30 AM by Odus.)
1028Intermediate
1029Globals
1030After implementing our two (well, three) methods, you might notice that we have 2x of the same constant 'legal_age'. We've kind of defeated the purpose of constants in the fact that they represent one value and is mapped throughout the program, so once change would affect everything.
1031
1032I'm pleased to say there's a solution of this. Thinking back to scope, a variable is only accessible in the same scope it was declared in. So what if we moved it up a level into the class? It's absolutely possible. Shall we do it? Yes!
1033
1034Take out the consts from the AgeDifference and CanDrive methods. Between the opening brace for the Program class and just above the Main method, paste it in. You'll see that all errors have suddenly disappeared. We've just declared the legal_age const in the class, so everything in the class scope (including methods and their sub-scopes) can access it. But we're not out of the woods yet, there are still some conventions & practices to adhere to.
1035
1036Rewrite your const with a private modifier, just like a method so other classes can't access it. We also want to rename it like so:
1037Quote:
1038LEGAL_AGE
1039
1040It's fully capped as a naming convention that global consts are fully capitalized and spaced with an underline. I'm guilty of this convention, others will have different preferences but this form is used in many languages. Since C# is case specific, renaming manually would mean we have to rename each reference to it in methods and whatnot.
1041
1042Fortunately, Visual Studio, the glorious IDE that it is, has provided us a shortcut. Hold Ctrl and double tap the R key, the alias will highlight green and you can retype as "LEGAL_AGE". All references to it throughout the entire program will be updated.
1043
1044However, global constants aren't always a good thing. Remember that everything in the class (or even outside) now has access to it and as a programmer, you want to keep things scoped as closely as possible. This could even include passing it as a parameter.
1045
1046If we were using a global variable (just like local, but same rules as global constant), these methods are free to change it as well. So avoid them when you can, this was just to show you they exist and how to use them.
1047
1048Parameters
1049Supplementary to methods are what's called parameters, also known as arguments (args). A method with parameters will take arguments. That's pretty much the difference.
1050
1051Think of parameters as variables with values already assigned to them that we use in our program. Cast your mind back to when we looked at calculating the perimeter of a rectangle. To be able to access this same 2(l+w) formula from other methods, we could use 2x globals, but this is excessively scoped. Meaning that other methods that won't potentially need access to the length and width of the rectangle now have access to it.
1052
1053Remember when we said a lot of programming involves working with data we don't know? This is one of the best examples I can give you. So we don't include globals, create a function that returns an int, and accepts 2 arguments (note the wording difference) for length and width, both of type int. We'll end up with something like this:
1054
1055Code
1056private static int CalculatePerimeter(int width, int length)
1057{
1058 return (length * 2) + (width * 2);
1059}
1060
1061I'm returning the product of the expression on the same line, which is just the same as writing:
1062Code
1063int result = (length * 2) + (width * 2);
1064return result;
1065
1066We are able to use these parameters just like local variables as well, they're not constants.
1067
1068This is known as passing by value, where we pass the value of the object to the method and the variable passed will not change. The other is passing as a reference (NOT the same concept as reference types) where it can be used and manipulated by the method.
1069
1070To demonstrate, we'll write a method named 'MultiplyWrite', accepting a parameter of type int but as a reference and named 'operand' - since it's the operand of this operation. It's identified from the 'ref' modifier and looks like this after multiplying and printing:
1071
1072Code
1073private static void MultiplyWrite(ref int operand)
1074{
1075 operand *= 3;
1076 Console.WriteLine(operand);
1077}
1078
1079Run it and it will input 15. Print out the starting variable named 'number' after calling MultiplyWrite and you'll notice it was also changed:
1080
1081[Image: DlS4hh5.png]
1082
1083Cool, right? But you'll almost never need to use something like this, only when working with multiple returns which we'll dig into in the Advanced section.
1084Classes
1085If you've enjoyed yourself so far, you're in for a real treat! We're about to get started on the Object Oriented Programming (OOP) paradigm. This is one of the most important aspects of intermediate programming, so we'll go over what it actually is before we use it.
1086
1087OOP is based on the concept of 'objects', which contain data (know as attributes) and and sub procedures (known as methods), together known as members [of an object]. Using the Console class which we're familiar with, it treats the console as an object.
1088
1089We can write to it and read from it using Write/ReadLine, which are methods of the Console class. It also contains attributes which provides information about itself, as well as giving us the option to change it. For example, we can get or set the background color of the console by using its BackgroundColor property. We can also change the title of the console like so:
1090Code
1091Console.Title = "My Console";
1092
1093Just like methods, we go down the chain using the period (.) which gives us access to an object's members. Objects don't have t always represent actions, they can be used simply as a data structure used to profile data on a subject.
1094
1095I think I'm starting to sound a bit textbook with this, so let me demonstrate on a data model for a student. Right click on your project in the solution explorer (CSConsole) -> Add -> Class. Name it 'Student' and create it. You'll see it looks very similar to the program's main class.
1096
1097We start by writing the skeleton of our class:
1098Code
1099public class Student
1100{
1101
1102}
1103
1104You've seen these keywords before, the only difference is the name of the class which is Student. Now it's time to think on what kind of information we would hold on a student. Name? Age? Test scores? Sure, we've used arrays before, lets write that in.
1105
1106Code
1107public class Student
1108{
1109 public string Name;
1110 public int Age;
1111
1112 public int[] Scores;
1113}
1114
1115Notice how there is no appearance of the keyword 'static' which we're used to now. This is because an object must be declared as one, if that makes sense. When we declare this, it is individual (like a real student). That's not to say we can't create many though.
1116
1117Just because we're holding data doesn't mean we can't work with it internally. Classes support methods which can use its own attributes. For example, lets write a method to calculate the average score. We calculate averages by adding all elements and dividing by the answer by the number of elements. Since there's no hard limit on scores, we will use this data as it's held.
1118
1119Inside a function named 'GetAverageScore', Create a variable of type int and name it 'averageScore'. Then, for each score in our public int array 'Scores', add it to the final score. Then, divide final score by the length of the Scores array. You should get this:
1120
1121[Image: mse9Zli.png]
1122
1123Remember the assignment operators? We're using them above. You'll also see the 'this' keyword which is used to refer to the current instance of the object. Notice it's semi-transparent/slightly darker. This is because it's optional (which it is), but to show you where these attributes are coming from, I've included it.
1124
1125Now it's time to take our new class for a spin. Just like a type, we declare it like so:
1126Code
1127Student std = new Student();
1128
1129Then we can start to fill in the properties by referencing our variable 'std' of type Student. Just like in the other classes, we go down the branch using dot notation:
1130
1131[Image: o0lBGlH.png]
1132
1133Properties
1134The public variables in our student class are exactly that: variables. We're now going to look at properties in a class. The difference at face value looks like this:
1135
1136[Image: GZQqs4M.png]
1137
1138The keywords here (literally) are get and set, known as accessors. Using properties gives us far more control as to how these properties can be used. We can moderate access, perform validation when updating values (null, less than X, etc.) and do underlying processing when setting values.
1139
1140Good OOP practice will see you using a private variable to read/write to and the property acting as a relay. Like so:
1141
1142Code
1143private string _name;
1144public string Name
1145{
1146 get
1147 {
1148 return _name;
1149 }
1150 set
1151 {
1152 _name = value;
1153 }
1154}
1155
1156With 'value' being the value passed when setting. As you can see, it's heavy on the lines if you keep a consistent syntax. Fortunately, C# has introduced auto-properties which means at compile time, these will be generated and implemented for you. If you're doing any kind of manual processing or want to relay something other than its value, you must take the manual route. So we'll use:
1157Code
1158public string Name { get; set; }
1159
1160Apply the getters and setters for each of your properties: Name, Age and Scores, then try it out. Results should be exactly the same... for now!
1161
1162We're now going to adjust the scope of our Age property. Since it isn't humanly possible to change your age or date of birth. To prevent any accidental changes, we'll make this read-only. It's actually very simple, all we're doing is removing the set accessor, therefore removing possibility of change. Like so:
1163Code
1164public int Age { get; }
1165
1166However, this introduces an error. Our existing code back in the main class is assigning a value to it. We still need to be able to set a value, how can we go about it? Find out how in the next chapter.
1167Constructors
1168We're going to take a look at constructors to rectify this little assignment error going on in our project. Or more specifically: instance constructors.
1169
1170A constructor is a method called when a class of some sort is instantiated. C# does not enforce a constructor, but the compiler includes one if no constructor exists in your class, and sets all values as default. First thing to do is remove the age assignment from the main class and get rid of that error.
1171
1172Specific to our Student class, our constructor looks like this:
1173Code
1174public Student()
1175{
1176
1177}
1178
1179Empty as a drum. We're now going to use the constructor to get one-time access to certain values in our class, the only one in this case being the Age property. Thinking back to parameters, they're exactly the same in constructors. Also remember the use of 'this' keyword to refer to the instance.
1180
1181This is how we'll set our Age:
1182
1183[Image: 0N4UFS1.png]
1184
1185As you can see, it's error-free. If we tried to set the Age property in our Student class anywhere outside the constructor, we'd get the same error as if trying to set it outside the class.
1186
1187Going back to our main class, we update the declaration to provide an int as the age. Like so:
1188
1189Code
1190Student std = new Student(18);
1191
1192It's important to keep the work in your constructor light, they should only be used to construct your class. Any heavy construction should be moved to an independent builder/factory method.
1193
1194Enumerations
1195Enums - short for enumerations - is essentially a grouped range of constants. It usually goes hand in hand with a type of some sort. It is declared like this:
1196
1197Code
1198public Enum Grades
1199{
1200
1201}
1202
1203Then, any sort of name you like can be included, so long as they are delimited with a comma. Below the first brace of the CSConsole namespace but above the Student class, declare an enum which looks like this:
1204
1205[Image: YRYD4Mi.png]
1206
1207In reality, these are just int typed constants, so these values can be overridden should we need to use their values directly. They support int, byte, long and short (all signed/unsigned). You'll also notice the pale/minty green of the Grade. That's syntax highlighting again.
1208
1209We're now going to implement a method inside our student class which will determine whether the student has passed or failed based on their scores. If the average score is above 70%, they have passed (we're a generous university, after all) and if anything lower, they've failed.
1210
1211It should return type Grade:
1212
1213[Image: UwMYl3j.png]
1214
1215Since we're calling GetAverageScores internally and no longer need to know the average score outside of the class, we can actually make the method private, by changing the access modifier 'public' to 'private'.
1216
1217Exceptions
1218Occasionally, things may go wrong and we need to know how to deal with this. What you know as a "crash" is known to programmers as an "unhandled exception." Meaning that something unexpected happened we haven't managed to catch. Before we look, it's important to know that making your program 100% robust is ridiculously hard, and it gets progressively harder as your program increases in size & functionality.
1219
1220To trap exceptions, we wrap potentially hazardous code in TryCatch blocks, which look like this:
1221
1222Code
1223try
1224{
1225
1226}
1227catch
1228{
1229
1230}
1231
1232In the event that something inside the Try block throw an exception, it will divert to the Catch block, which gives you the opportunity to handle and possibly even recover from errors. We're going to demonstrate the latter in our student class.
1233
1234So you get an idea of what a crash looks like when a debugger is attached, modify your code in the main class and take out the Scores setter, leaving you with this:
1235Code
1236Student std = new Student(18);
1237std.Name = "Odus";
1238
1239Console.WriteLine(std.GetGrade());
1240
1241Run it and you'll notice a pause in the WriteLine before it crashed and brings up the crash report. The window looks like this: (Click to View)
1242
1243The exception was thrown inside the Student class, which is better for us since it gives us a chance to do the error handling backend. The TryCatch block allows us to catch specific exceptions, offering multiple paths of handling depending on the exception. First, wrap the method in inside the Try braces and place a Catch below.
1244
1245We're also able to supply a 'parameter' for the exception thrown which tells us the type and gives us other information such as the description, stack trace and whatnot. But we won't in this case and you'll see why soon. Inside the catch block for a NullReferenceException, return -1.
1246
1247Returning -1 means that all code paths in the method return some kind of value, and negative 1 is perfect since the minimum score is 0, and an average of 3x 0's will still give you 0, it's not possible to achieve a negative score (theoretically).
1248
1249We need to continue handling this inside our GetGrade method. So we'll first add another const to our Grade enum named 'None'.
1250
1251Code
1252public enum Grade
1253{
1254 Pass,
1255 Fail,
1256 None
1257}
1258
1259Back to GetGrade, we're going to catch exactly negative 1. In the odd chance a school using this would use a weighted grading system that can produce negatives, it will still handle it and the odds of -1 are extremely rare.
1260
1261Since we're going to be evaluating GetAverageScore() more than once, we'll move it to a variable named 'averageScore' of type int and change our conditional to evaluate that instead of a call to the method itself. Between the first and else condition, insert an else-if clause that checked 'averageScore' for -1. If it does, return Grade.None.
1262
1263This way, anything over 70 is a pass, anything -1 is no grade, and anything else is a fail. Your new GetGrade method should look like this:
1264
1265[Image: ISjFqbn.png]
1266
1267As a final note, it's better to perform checks like this manually rather than relying on a Catch block to recover from an error. For example, checking if the Scores array was null would mean we didn't have to include a TryCatch block in our method. I used it here because it was a great example of accounting for errors.
1268
1269Ternaries
1270We're going to take a look at ternaries, which is essentially a condensed if-statement. Going back to our beginner project of being able to drive, don't worry I don't expect you to have retained that code, we done something like this:
1271
1272Code
1273if (age >= legal_age)
1274{
1275 Console.WriteLine("You are old enough to drive");
1276}
1277else
1278{
1279 Console.WriteLine("You are old enough to drive");
1280}
1281
1282The difference is small, so we can take a shortcut by using a ternary, turning it into this:
1283
1284[Image: PWbXkOk.png]
1285
1286As you can see, we get a fairly longer line, but it means we're not writing out a huge If statement which makes little difference in its result. Lets dissect the ternary:
1287
1288Code
1289(condition ? trueExp : falseExp)
1290condition
1291Identical to a logical expression in the standard If
1292trueExp
1293Expression to execute if true
1294trueExp
1295Expression to execute if false
1296
1297As you can see, not very different, but saves a fair amount of space and in cases it's designed for, means you write less. Although this is available to you, don't go overboard with it and especially don't ever nest ternaries. They work, but ternaries impact readability and even more so when nested.
1298
1299This concludes our Intermediate section. The next will cover more advanced topics and it's only worth a read after a fair amount of practice on all previous sections. Good luck!
1300Find Reply Quote Report
1301Odus
1302Odus Away
1303[closed@HF:]
1304Posts: 2,977
1305Threads: 83
1306Popularity: 12
1307βytes: β 0
1308 #5 02-19-2016, 08:23 PM (This post was last modified: 03-05-2016, 10:16 AM by Odus.)
1309Advanced
1310Parameters II
1311We'll ease into more advanced topics by using the previous Parameters chapter as a bridge between intermediate and advanced. The same applies to the following two or so chapters.
1312
1313Third type of parameter is called an out-parameter.
1314
1315It works similarly to ref which we looked at previously, only that out is solely for projection, ref works for intake as well. There's no better example than TryParse, we'll use the Int32 (alias: int) one which looks like this:
1316
1317[Image: ZZ0XruJ.png]
1318
1319Notice the difference in keywords, 'out' tells us that the variable's current value is irrelevant and plays no part in the method other than an assignment. If it were ref, the user (programmer) could be confused into thinking it mattered what they sent along with the call.
1320
1321All being well, our result int would hold 123 and our bool would return true. This ties into exception handling as well, since avoiding exceptions is the even better choice as I covered earlier.
1322
1323String Interpolation
1324Until now, we've been using the concatenation operator (+) for our string operations. But what if we want to build more complex strings? A lot of concatenations will produce very long lines of code, even when split across multiple lines. Fortunately, we have a little thing called string interpolation.
1325
1326C# 6.0 revisited string interpolation, building strings looks like this:
1327
1328[Image: p4gNoME.png]
1329
1330Notice the dollar sign prefix? This tells the editor that we will be building a dynamic string and to open up instance members when we insert curly braces. When executed, this will be written to the console:
1331Quote:
1332Student name: Odus
1333Student age: 18
1334
1335I can tell what you're thinking: "How did it take a new line?" and it's simple. Look between them and you can see "\n". This is the next part of building strings and known as escape sequences. As you may have guessed, 'n' stands for new line. There are a huge bunch of these, but here's the chart: https://msdn.microsoft.com/en-us/library/h21280bw.aspx
1336
1337But what if we don't want it to recognize escape sequences? We can mark a string as literal instead of verbatim by prefixing the @ symbol to the string before the speech marks:
1338
1339Code
1340Console.WriteLine(@"test\tone");
1341//Produces "test\tone" instead of interpreting "\t" as a tab
1342
1343Inheritance
1344Going back to the OOP series of chapters from the previous section, we're taking a look a concept called inheritance. Inheritance allows new classes to inherit the members of a previous class. So when you subclass by making a new class that inherits from another, all its methods and properties become available to use in that class. Think of it as an extension. To demonstrate, we'll return to our Student class and add a constructor overload. The code for the Student class is below.
1345
1346Code
1347using System;
1348
1349namespace CSConsole
1350{
1351 public enum Grade
1352 {
1353 Pass,
1354 Fail,
1355 None
1356 }
1357
1358 public class Student
1359 {
1360 public string Name { get; set; }
1361
1362 public int Age { get; set; }
1363
1364 public int[] Scores { get; set; }
1365
1366 public Grade GetGrade()
1367 {
1368 int averageScore = GetAverageScore();
1369
1370 if (averageScore > 70)
1371 {
1372 return Grade.Pass;
1373 }
1374 else if (averageScore == -1)
1375 {
1376 return Grade.None;
1377 }
1378 else
1379 {
1380 return Grade.Fail;
1381 }
1382 }
1383
1384 private int GetAverageScore()
1385 {
1386 int averageScore = 0;
1387
1388 try
1389 {
1390 foreach (int score in this.Scores)
1391 {
1392 averageScore += score;
1393 }
1394
1395 averageScore /= this.Scores.Length;
1396
1397 return averageScore;
1398 }
1399 catch (NullReferenceException)
1400 {
1401 return -1;
1402 }
1403 }
1404
1405 public override string ToString()
1406 {
1407 return $"{Name}, aged {Age}, has an average score of: {GetAverageScore()}%";
1408 }
1409 }
1410}
1411
1412Lets say this represents the attributes of a University student and this University wants to accept exchange students, but must record a log of the institution they've exchanged with and their own student at that institution. We could record this in a misc. notes field of the Student class, or we could inherit and subclass. To do this, create a new class file in your project (right click project -> Add -> Class) and name it ExchangeStudent. Keeping everything, you want to append the inherit marker and the class to inherit after the class name, like so:
1413
1414Code
1415public class ExchangeStudent: Student
1416{
1417
1418}
1419
1420We're now going to include properties for the exchanged institution and the student that was exchanged. That's right, we can even create properties of custom types/structures. Like so:
1421Code
1422public string Institution { get; set; }
1423public Student Exchangee { get; set; }
1424
1425If we return to our Main method and create a new ExchangeStudent and begin to populate it, you'll see that inherited superclass members (Name, Age, Scores, GetAverageScore(), etc.) are available as well as our newly set properties in the ExchangeStudent subclass.
1426
1427[Image: z7nv7Yh.png]
1428Overloads
1429Before we go any further, it's time we learned about overloads. An overload is a method sporting the same alias as another but accepting different parameters, and so doing (usually) slightly different work.
1430
1431Using our perimeter function again as an example, this time it's overloaded to support types int and double:
1432
1433[Image: NJ8N9bD.png]
1434
1435Using it is no different than a regular method. Which method you are calling is even identifiable in the code editor. For example:
1436Code
1437CalculatePerimeter(2, 2);
1438
1439Has detected by the arguments that it will be calling the function that returns an int. If we were to introduce real numbers (irrational), it would infer its double counterpart:
1440Code
1441CalculatePerimiter(2.3, 2.5);
1442
1443One of the common places you'll see overloaded methods are constructors. Having multiple constructors available means the class can be more flexible in how it's used.
1444
1445We're going to include another constructor in our Student class that takes a parameter of type DateTime named 'dob'. To overload, begin writing out the constructor again and fill in the code below. Your sibling constructors should look like this (subtracting 1 to assume no birthday yet):
1446
1447Code
1448public Student(int age)
1449{
1450 this.Age = age;
1451}
1452
1453public Student(DateTime dob)
1454{
1455 this.Age = DateTime.Now.Year - dob.Year - 1;
1456}
1457
1458When rewritten to accept our new constructor, it looks like this:
1459
1460[Image: Cl5FQoN.png]
1461
1462Overrides
1463Continuing with the Student class, we're now going to talk about overriding methods which are different to overloads - don't confuse them! This will come more into play with more complicate OOP designs. Methods can be overridden to implement it yourself, whilst still retaining the method name that wouldn't be available since it's part of its inheritor.
1464
1465For example, Object is the base class of pretty much all types, including our Student class. Object contains a method named ToString, which converts (or displays) the string representation of that object.
1466
1467If we were to do something like:
1468Code
1469Console.WriteLine(std.ToString());
1470
1471It would only print its location in the namespace and name of the class. We must implement it ourselves in order to change it. If we wrote it as normal, a warning would flag informing us of this inheritance conflict:
1472
1473[Image: N6fOLLT.png]
1474
1475When allowing IntelliSense to fill in the method for us, we get the default implementation:
1476
1477Code
1478public override string ToString()
1479{
1480 return base.ToString();
1481}
1482
1483Now we can return anything we like when ToString is called. We'll use ToString as a default information print that returns:
1484Quote:
1485"<Name>, aged <Age>, has an average score of: <averageScore>%"
1486
1487Convert this to an interpolated string. Collapse the spoiler if you're having trouble. (Click to View)
1488
1489Which if we run, would print:
1490
1491[Image: eX2powh.png]
1492
1493Since the GetAverageScore member is private, we can still access it internally. Something like this may be good for debugging so we've included the student's average score.
1494
1495File Input/Output
1496You'll often hear the contraction "IO" which stands for input/output and this can be used to represent many things, but we'll be looking at local files. But before we can actually work with files, it's important to talk about what they actually are.
1497
1498Until now, you've probably assumed files are unique in how they are written to storage. Files are actually all the same in the sense that they are just stored data. How it's formatted and its content is what makes it unique. Even something like a Word document file is a series of grouped files containing info about the document, such as its text, fonts, images, etc.
1499
1500The important thing is that you realize files are unique in the sense that they're different. Weird way to put it, I know. So we'll only cover basic TXT files: files with no formatting or preservation, solely text.
1501
1502When a file is read or written to, it's done via a stream. This stream represents a sequence of bytes being read or written.
1503
1504We can harness the power of the .NET framework and use its classes for IO, conveniently located inside the System.IO namespace, so reference it at the top of your project:
1505Code
1506using System.IO;
1507
1508There are easy methods such as WriteAllText/Bytes/Lines, but we're going to be using the StreamWriter to write our files, giving us more control and since these methods use StreamWriter internally, it's better for us to see what's going on for a first look.
1509
1510To get started, paste the code below and I'll explain what's going on.
1511
1512Code
1513string directory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
1514
1515using (StreamWriter sw = new StreamWriter(directory + @"\file.txt"))
1516{
1517 sw.WriteLine("test");
1518}
1519
1520At the very top we've declared a string to fetch the desktop directory. These are part of the System.Environment class which gives us all sorts of tools for working with the local machine. SpecialFolder is an enum, we've worked with those before. Calling GetFolderPath and supplying it translates what the enum represents into a working, absolute directory.
1521
1522Next we're opening a Using construct. These are used to mark the object for disposal after we've used it so as not to waste resources. Everything inside the braces is scoped to that StreamReader we've instantiated as 'sw'. It means closing the file is handled for us as well.
1523
1524Inside the constructor for StreamReader we've passed a path. One of the constructors accepts this (more on the next chapter) and it will create the file if it does not already exist. Notice the literal (@) marker? We could do it without, but we'd have to use "\\" instead of a single slash.
1525
1526Inside, it's easy to think of it as an external console. The same method [names] are available to us: Write, WriteLine, etc.
1527
1528Working with it is very simple:
1529Code
1530sw.WriteLine("Hello Desktop!");
1531
1532Reading from a file a similar story, but there are key differences, particularly when it comes to processing large quantities of data. Instead of reading it all into memory at once using ReadToEnd(), we can loop through the stream until the end (as marked by The StreamReader's EndOfStream property) and read each line individually. Like so:
1533
1534[Image: rGKKP11.png]
1535
1536Try it for yourself. It will read the same line "test" from the file. Those are the essentials of working with files. How you process them and what you do with them will vary.
1537
1538Collections II
1539XXX
1540Pointers
1541Moving on to more advanced stuff, we're going to talk about pointers and unsafe code. When code is explicitly marked as unsafe, it is unmanaged, meaning it's compiled directly to machine code. Managed code (what we've been using so far) where lots of background management is present which looks after memory, ensures type safety, etc. All of this is considerably reduced if not omitted with unsafe code.
1542
1543Using unsafe code is not recommended, but available should you be working very low level, complex PInvoke (which we'll learn about nex) calls or when performance down to the nanosecond is crucial. None of which are really common.
1544
1545Pointers are only available in primitive types, including: byte, short, int, long, char, float, double, decimal and bool. We lose string since it's not entirely primitive which we talked about at the start.
1546
1547Enough lecturing, time to take a look. We must first set the unsafe flag for the compiler as it prohibits unsafe code by default. To do this, double click on Properties in the solution explorer, head to Build and check the "Allow unsafe code" checkbox:
1548
1549[Image: CY0glkl.png]
1550
1551When working with inner unsafe code, we must mark it within an Unsafe zone. Just like a Using construct.
1552
1553Code
1554unsafe
1555{
1556
1557}
1558
1559The unsafe keyword can be applied in local (using the above block), method and class level context. Marking an entire class as unsafe removes the need to qualify any unsafe methods or blocks inside safe methods.
1560
1561Pointers essentially store the location of a value and serve to point to that address. Since we've yet to use the byte data type, we're going to test this out by halving a byte and creating a method to do so. Remember, since we're pointing to the value, we can update it directly, therefore we write this as a void.
1562
1563[Image: k5PcG3n.png]
1564
1565You will notice the asterisk bolted to the end of the type, this is how we identify pointers. We continue to qualify with the asterisk inside the method, this time preceding the alias. Without the cast back to byte, that expression would produce an int.
1566
1567Now to use this function. we'll set up a variable named 'b' of type byte, and give it a value of 6. Then, create an unsafe context inside your Main method and call HalveByte inside its body like so:
1568
1569Code
1570unsafe
1571{
1572 HalveByte(&b);
1573}
1574
1575There is an ampersand before the alias, and that means we're representing the address of the byte, not its actual value. Again, this is only available in unsafe context. Your code should look like this:
1576
1577[Image: pnXqV4B.png]
1578
1579Run it and you'll find that its value is 3. Now you're probably asking, why would we go to so much trouble? The difference, as I mentioned above, is performance. Here we are in almost total control with little intervention from any .NET runtime assistants.
1580
1581This shows how pointers are being used here to update the value at its memory location (remember, that method is a void). However, it doesn't fully explain why it's marked as 'unsafe'. We're now going to take a look at printing an array in unsafe context.
1582
1583Create an unsafe void named 'PrintArray', accepting a pointer of type int (not an array) and another named 'len' to represent the array's length. We're losing all .NET type safety with unsafe code, so there's no way to tell when to stop. Therefore we must supply the array's length from safe context.
1584
1585We can then create a standard For loop, using an iterator variable of type int, named 'i', and running while it's less then our len parameter. This is what you should have:
1586
1587[Image: k1LjP6O.png]
1588
1589We'll half it and print to the console like so:
1590Code
1591Console.WriteLine(*(arr + i) / 2);
1592
1593Parameter 'arr' representing the base address, and iterator 'i' as offset. Just like indexing (which is also supported, but again, we're going unsafe so I want to show you everything).
1594
1595Attempting to use this would not work, primarily because of the error but also because we're using unsafe. We need to protect this array by reserving its location in memory.
1596
1597To work with this, it's time to introduce another keyword: fixed. Since we're working with address only and not its actual contents, using this keyword stops memory management from transferring its location across the heap. When implemented, your final code will look like this:
1598
1599[Image: Ciwf9Rh.png]
1600
1601Running this will print to the console:
1602Quote:
16031
16042
16053
16064
1607
1608All well and good. But this still hasn't shown us why unsafe is truly unsafe. Replace 'len' in the loop with the int: 10. It's well outside the bounds of our byte array (4 places). If you run it now, it will continue to call addresses on that offset. For example, I get this:
1609
1610[Image: Kiy0tPz.png]
1611
1612Nothing after 4 is inside my array. Now you can see that without type safety, it hasn't thrown an out of bounds exception and we're accessing memory locations that aren't entirely our own.
1613
1614PInvoke
1615Short for "Platform Invocation", this functionality allows us to call unmanaged external functions from a library (.dll) from our own managed application. It was developed back when the .NET framework was still new so it offered the same flexibility. As it matures, more and more external functions are being implemented to the framework so the likelihood of needing this lessens. But it's not fully covered, so we may still want to use it sometimes.
1616
1617There's no need to set any markers for unsafe code this time since it's working externally, all we need is to reference System.Runtime.InteropServices.
1618
1619In this example, we're going to sound a beep like when a message box appears on form applications. To do this, we must first reference where the function is housed. In this case, it's the user32 lib and looks like this:
1620Code
1621[DllImport("user32.dll")]
1622static extern bool MessageBeep(uint uType);
1623
1624This function is accepting a parameter for the beep type (alert, ok, exclamation, standard, etc.) but how did we know? There's a whole wiki dedicated to PInvoke and it is: http://pinvoke.net/
1625
1626This site holds every discovered (and workin) PInvoker available for .NET and is user-contributed. Since documentation can be low or otherwise unavailable, it can be difficult to get it exactly right. What's available there is tried and tested. For example, our MessageBeep function is recorded here: http://www.pinvoke.net/default.aspx/user32.messagebeep
1627
1628We've already discussed static modifiers, the new one here is 'extern', which indicates to the compiler that the method is external to the application. As you can see, this unmanaged function also returns a bool, likely representing its success. Its PInvoke article includes it in a class which contains an enum for the type of beep, but we'll take the standard one and supply 0xFF as an argument.
1629
1630When written, your code would look like this:
1631
1632[Image: ilRQnzY.png]
1633
1634As you can see, it's called just like a method from our own code. The applications of PInvoke are endless, spend some time on the wiki and you'll see just what it can offer.
1635
1636Threading (Under Construction)
1637
1638As of now, our applications have ran solely on a single thread, known as the main thread. If we're doing any intensive work, the thread will become occupied and since it also handles the UI, the UI will hang, giving the impression that the program has crashed. This is more visible in windows forms rather than console. This is where threading comes in, since we now have the opportunity to divide and conquer within our program (meaning break it into smaller parts!).
1639
1640For this, we'll need to reference the threading namespace:
1641Code
1642using System.Threading;
1643
1644Going back to our trusty old-enough-to-drive scenario, create a method named CheckAge, with a parameter of type object and call it 'data'. This is the data (sometimes known as 'state') that's passed to the thread. It's mandatory to pass as obejct, but we can cast it back to int in the method.
1645
1646I've wrote mine like so:
1647Code
1648private static void CheckAge(object data)
1649{
1650 int age = (int)data;
1651
1652 Console.WriteLine($"You are no{(age < 16 ? 't' : 'w')} old enough to drive.");
1653}
1654
1655Notice the inline ternary? It's going to either print "You are now old enough to drive" or "You are not old enough to drive". Saves a lot of time typing out a full conditional for very little difference in results. Now to declare our thread:
1656Code
1657Thread ageCheck = new Thread(CheckAge);
1658ageCheck.IsBackground = true;
1659
1660In this example, the thread's IsBackground property has been set to true, meaning it's a background thread. This kind of thread will terminate when its creator/the main thread terminates. Unlike foreground threads, which could still be running after the application has exited and can sometimes prevent it from closing.
1661
1662To start, we can simply call Start(), which supports an overload that accepts a parameter to pass to our ThreadStart:
1663Code
1664ageCheck.Start(18);
1665
1666Running this, you can see there's no measurable difference, but the age calculation was done on another thread.
1667
1668
1669
1670That concludes our tutorial. I hope you've learned something new if you already had experience, and I wish you the best of luck in programming if you're a newbie or not. Please leave some feedback if you enjoyed it or spotted a mistake. :)