Getting Started Learning C# the Hard [sic] Way

Introduction: Getting Started Learning C# the Hard [sic] Way

So you want to learn to program in C# huh? This is the first in a series of guides that will show you how to do exactly that, but do so requiring only the simplest of tools and the most basic of setups, at no cost to you.

I believe in learning by doing and leveraging the experience of others.

For these reasons, I am going to borrow a pedagogical method and a value system from the learncodethehardway.org books.

You should be able to get by on nothing more than the tools a programming language ships with. Reducing your dependance on particular tools actually increases your options, which is also a good argument for learning more than one programming language, eventually.

I am not saying your life will ever depend on knowing how to write a program with nothing more than csc.exe and notepad in a stuck elevator in a building on fire with no internet connection, but if it did, wouldn't you like to be not completely lost in that article

Let's get started. You are going to need a few things and going to have to do some small chores before we can start coding in C#.

The first thing we'll need is CSharpRepl. It is an interactive programming environment for C# that ships with Mono. You can download it here. As of the time of this writing, you are going to want to download Mono 5.0.1.

Alternatively, if you are on a Mac with homebrew installed, you can simply:

brew install mono



Submitted by Brazos Valley Makerspace for the Instructables Sponsorship Program

Step 1: REPL-ing

To start the REPL (read-eval-print loop), open a command prompt.

On Windows: press the Windows Key and R together to bring up the run box, then type in "cmd". Then navigate to the folder you installed Mono to by issuing a command like cd "C:\Program Files (x86)\Mono-3.0.10\bin". NOTE: the path to where your Mono executable files live may be different than mine, especially if you downloaded a newer version of Mono.

Alternatively, there is a mono command line. You may find it by typing "mono" into the search box of your start menu (Windows Vista or newer).

To run the REPL, type "csharp" in the new window you just launched with the black background and blinking white cursor (cmd.exe a.k.a. Command Prompt) and press the enter key.

On OS X: press Command key and the space bar together to bring up Spotlight. Then type in "Terminal" and press the Enter key.

When you pressed the Windows Key and the R key together (or Command and Space), you used what is called a keyboard shortcut. Throughout this guide, and related guides, keyboard shortcuts will be represented in the form Key + Key (for example you just used Windows + R).

On either platform:
Now, you should see:

csharp>

followed by a blinking cursor.

Type:

1+1; 

and press the enter key. You should see ">2" on the screen. Congratulations! You've just written your first bit of C#.

A bit of housekeeping for Windows Users

You aren't going to want to navigate to the Mono bin directory every time you start the REPL. So we are going to add the path to the Mono bin directory to your system Path environment variable. I've added some images to this step to try to illustrate the process. I'm using Windows 7. It may look slightly different on your system.

  1. Open the Control Panel
  2. Search for "Environment"
  3. Click "Edit the system environment varialbles" link
  4. Click the "Environment Variables..." button
  5. In the list labeled "System variable", find Path
  6. Click on Path
  7. Click on the "Edit..." button
  8. At the very end of the box labeled "Variable value", type a semi-colon if there isn't one already there (at the end)
  9. Type the path to your Mono installation's bin folder (more than likely C:\Program Files\Mono\bin)
  10. Click "OK" three times
  11. Open the Run Box (Windows Key + R)
  12. Type cmd into the Run Box and press Enter
  13. Type echo %Path% into the command prompt and press Enter
  14. You should see the path to the Mono bin folder printed to the screen
  15. Type csharp into the command prompt and press Enter
  16. You should now be looking at the Mono C# REPL prompt

Useful commands

Typing this into the REPL will clear the screen:

System.Console.Clear()

Typing this into the REPL will print the online help:

help;

Typing this into the REPL will quit the REPL:

quit;

Step 2: Express[ion] Yourself

The text you just typed "1+1" is called an expression, Expressions are the most basic building blocks of computer programs.

Now type "var i =0;" at the cursor and hit the enter key. What you just typed is called an assignment expression. That means you created a variable (an integer variable in this case) and assigned the value 1 to it.

You might be thinking, "That's nice, but what good is it?" Just a minute, and I'll show you.

Type the following:

Console.WriteLine(i);

and press the enter key. Now you should see a 0 on the following line. The last instruction you gave to the REPL is what's called a method expression.

As you are working along in the REPL, you may feel like clearing the screen. Typing:

Console.Clear(); 

and pressing Enter will do just that.

From here on out, when I instruct you to type some text into the command prompt, I am also implying that you need to press the Enter key after doing so.

Step 3: Handling the Unexpected

Type the following:

Console.WriteLine("Foo"

then press Enter.

You should see something like this:

csharp>Console.WriteLine("Foo"
>

Here the REPL is giving you a chance to finish the expression. Try typing a ")" and pressing Enter.

The REPL should now look as follows:

csharp>Console.WriteLine("Foo"
>)

If the REPL shouts some technobabble at you, just copy the english looking portions of it into a browser search box and hit enter. The ability to perform internet searches well is actually a very important skill for any programmer (hobbyist or otherwise) to have.

Let's try deliberately causing an error.

Type:

csharp>Console.WriteLine("Bar

and press Enter.

You may have noticed that when we are writing text for the computer to do something with, we enclose it in typewriter (a.k.a. straight or dumb) quotes (e.g. "Foo" above). The compiler generally doesn't like it when there is a line break in text (a string literal in programming parlance).

So, lets type the ending typewriter quote mark, skip a line, type the closing ")" and see what happens:

    >"
    >)

Now you should see something like the following:

csharp> Console.WriteLine("Bar
>" > ) (1,23): error CS1010: Newline in constant (2,1): error CS1010: Newline in constant

A quick internet search for "CS1010: Newline in constant" will turn up this: https://docs.microsoft.com/en-us/dotnet/csharp/mis... Perhaps not the best explanation, but it gets the point across. Websites such as stackoverflow.com may be more useful than the actual documentation.

Try the following instead:

csharp> Console.WriteLine(@"Foo
> Bar > ")

The result should look like:

Foo
Bar

The @ before the typewriter quote is used to create a verbatim string literal. Among other things a verbatim string literal allows for line breaks in strings. You can read more about verbatim string literals and string literals in general at https://www.dotnetperls.com/string-literal

You have written arithmetic expressions, assignment expressions, and method expressions. You are well on you way to being able to write a computer program.

Step 4: Keeping It Classy

C# and Java are object oriented to a fault. That is, in order to do anything useful with them, you have to have an object. To get an object, you need a class. A class is like a mold for making objects.

Fire up the csharp REPL, and lets define a really simple class:

csharp> public class Person
> { > } >

That's the simplest class definition you will ever see. It is also not very useful.
Continuing on, we'll add fields to classes as a means to store information.

Step 5: Getting Methodical

First, kill your current REPL session. You may achieve this by typing:

Environment.Exit(0)

A convenient way to do the same thing that is baked into the REPL is to type:

quit;

Now you should see the normal command prompt. Start a new session. You may do this by typing "cs" then pressing the tab key (on OS X you may have to type "csha"). "csharp" should now appear on your screen. What just happened is called tab completion, it is a feature of the Command Prompt, Linux Consoles, and some text editors. It can be very useful, especially if you are not a big fan of typing the same thing over and over again. Start the new session by pressing the enter key.

Now type the following:

csharp> using System.Text;
> class Greetings >{ > public void SayHi() > { > var sb = new StringBuilder(); > sb.Append('H'); > sb.Append('e'); > sb.Append('l');

Now rather than typing "sb.Append('l');" a second time. Hit the up arrow if you have a direction pad (a.k.a. dpad) on your keyboard.

The command line should now look like:

>        sb.AppendLine('l');

Now type the remaining lines below to complete our class with it's one method:

> sb.Append('l');
> sb.Append('o'); > sb.Append(" world"); > Console.Write(sb.ToString()); > } >}

We can make use of what we just wrote by typing the following:

<p>> var greetings = new Greetings();<br>> greetings.SayHi();</p>

What just happened is that you wrote a class called Greetings containing a method called SayHi. Then you created an instance of that class and called (invoked) its SayHi method. A method is basically just a convenient way to group a set of instructions (statements) together and provide a means of executing those instructions.
If you are familiar with a structured programming language such as C, methods are basically just functions that belong to classes or structs or objects (instances of a class or struct). Methods allow objects to do things (behavior). Now lets move on to fields, which allow objects to know things (state).

Step 6: Taking the Field

Now for something different. Type the following into the REPL

class Person
{ public string HairColor; public Person(string hairColor) { HairColor = hairColor; } }

var robert = new Person("brown");

print(robert.HairColor);

The previous code example creates a class named Person, defines a constructor (constructors are methods that always have the same name as the class they belong to and no return type because it is implied), and defines a field named HairColor. The code also creates a Person object with brown HairColor. The constructor takes one parameter (a.k.a. argument) and uses that parameter to set the value of the HairColor field.
Classes are basically descriptions of objects. Creating an instance of a class is like making a part from a mold. Classes can be thought of as a description of a particular kind of object. In programming, objects are things that contain state and behavior. State is a computer science-esque way of saying condition. In the real world, some conditions are permanent while others are temporary. The same is true of the conditions (state) of objects. For instance. At the time of this writing, if I were to create an object to represent myself, if the type of object I'd created had a field named "HairColor" I would set its value to be brown. In ten years, I'd probably have to set its value to be grey. Fields are one way to represent state in classes. Fields define attributes of classes. Fields can be public (visible from anywhere in the system—they can be accessed or modified from any part of the program) or private. There are other visibility modifiers, but you can learn more about them on your own. The convention in C# is to leave fields private, unless they are constants (their value cannot be changed), and to access or change their value using properties or methods. Try this:

class Person
{ private string hairColor;

public void SetHairColor(string color) { hairColor = color; } }

Oops! You should have seen a message like the following after you pressed enter the last time:
warning CS0414: The private field `Person.hairColor' is assigned but its value is never used Having a field that we can set but can't use isn't very helpful. Try this instead:

class Person
{ private string hairColor; public void SetHairColor(string color) { hairColor = color; }

public string GetHairColor() { return hairColor; } }

var robert = new Person(); robert.SetHairColor("maroon"); robert.GetHairColor();

After that call to robert.GetHairColor(), you should see the text "maroon" parroted back to you by the REPL. That SetHairColor method seems like it would come in pretty useful on game day.
What happens if you try typing the following after the example above:

print(robert.hairColor);

You should see a message like the following:
error CS0122: `Person.hairColor' is inaccessible due to its protection level That simply means that hairColor isn't public anymore. Now that we have some idea about fields and how they are used, let's move on to properties.

Step 7: Other People's Properties

Let's look at some other ways to set the state of our objects. Enter the following code snippet into the REPL:

class Inbox
{ public int MessageCount {get; set;} }

var inbox = new Inbox(); inbox.MessageCount = 1; print(inbox.MessageCount);

After the print(inbox.MessageCount); line you should see a "1". Let's review. You created a class named Inbox and gave it an integer property named MessageCount. Specifically you gave it an auto-property.
Another way to create an equivalent property would have been:

private int messageCount;
public int MessageCount { get { return messageCount; } set { messageCount = value; } }

With auto properties, the compiler creates what is known as the backing field (less commonly known as a backing store) for you.
As you can see by the use of return in the get above, properties are basically just a shorthand for methods in C#. They take the place of getter/setter methods in languages that do not support them like Java. Properties can have a getter and a setter, only a getter, or only a setter. It is common to see properties with a getter an no setter where implementing a setter would break the encapsulation of the class implementing it. For instance, you can give a puppy food, but you can't change it's height. The puppy does that on its own. When you're implementing properties and methods, ask yourself if it makes sense for something else to be able to change the state of your object. Let's try out the following:

class Inbox
{ public int MessageCount { get; private set;} public ArrayList Messages {get;} public void AddMessage(string message){ Messages.Add(message); MessageCount++; }

public Inbox() { Messages = new ArrayList(); } } var inbox = new Inbox(); inbox.AddMessage("Hello There!"); print(inbox.MessageCount); print(inbox.Messages[0]);

Let's examine what just happened. We created a class called Inbox. This Inbox class contains a List called Messages and method called AddMessage to add messages to that list. The Messages list is a specific instantiation of the Generic List class. The generic list class takes a type parameter. That's just a fancy way of saying the generic list class expects you to tell it what kind of objects it is going to contain. In this case, the type parameter is string. We also printed the first item in the Messages list to the screen. The left and right square brackets ("[" and "]") after Messages is called an indexer. An indexer is a special type of property that is conventionally used to retrieve items by using a key. In this case, the key is a numerical index. Note that indices in collections in C# are zero-based. That's just a fancy way of saying that the first item will be at index 0 rather than at index 1.

The default protection level of a getter is public. So, that means code that doesn't belong to the Inbox class can access the messages list.
Let's try the following:

> inbox.Messages.Add("Another Message");
> print(inbox.MessageCount); 1 > print(inbox.Messages[0]); Hello There! > print(inbox.Messages[1]); Another Message

Oops, that's not right. One really nasty side effect of making of your properties publicly accessible (especially if they are collections) is that your object can be placed into a state where it is inconsistent with itself.

Now that we've encountered collections, they probably deserve a more formal introduction.

Step 8: Collecting Speed

Perhaps by the very nature of programming—specifically its use in handling large quantities of data, operations, or things—a good understanding of collections, how they work, and what types are available is essential to good programming practice.
Let's revisit the list:

class MessageBox
{ private List messages = new List();

public int MessageCount { get { return messages.Count; } }

public string this [int i] { get { return messages[i]; } set { messages[i] = value; } }

The messages list is a specific instantiation of the generic List class. The generic list class takes a type parameter. That's just a fancy way of saying the generic List class expects you to tell it what kind of objects it is going to contain. In this case, the type parameter is string.
NOTE: List is pronounced "List of string". An unbound generic list (or list with no type parameter) is pronounced "List of T" where T is just a placeholder for the type that will be eventually bound to the generic class. Ok lets try to do something with our inbox.

csharp> var msgBox = MessageBox()
csharp> msgBox[0] = "A message" System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.

Well, that's not very useful. But it is instructive. We have to be very careful with indexers. Lists are initialized to have a size of zero, so we can't swap out an item at index if there's nothing at that index.

Copy the class definition from here and paste it into the REPL.

Copy/pasting into a terminal is a little easier on OS X, but it can still be done in Windows. You simply have to right-click the cmd.exe window and select "Paste" from the context menu.

Add, Find, and Remove methods and index operators wrapping container logic make it possible to easily swap out containers should the need arise. Not to mention that feature envy is just gross.

Lets exercise our new MessageBox class:

csharp> var msgBox = new MessageBox();
csharp> msgBox.AddMessage("Message 1"); csharp> msgBox.AddMessage("Message 2"); csharp> msgBox.AddMessage("Message 3"); csharp> print(msgBox) Message 1 Message 2 Message 3 csharp> msgBox.RemoveMessage("Message 2") csharp> print(msgBox) Message 1 Message 3

Say that we want to make our inbox give us messages in reverse chronological order, then we could use a stack instead. A stack is what is known as a LIFO (last in-first out) container.

Quit the REPL by typing quit; and pressing the Enter key.

As an alternative to pasting large amounts of text into the REPL, it supports a feature called Startup Files. This will let us save things like class definitions into files that end in .cs that are loaded when the REPL starts.

In OS X, the folder to place your .cs files into is ~/.config/csharp.

On Windows, it is slightly tricker, but not difficult. You simply type the following in to the REPL:

Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)

That will print the folder path of your AppData folder to the REPL.

Typing the following into the REPL will actually open the folder you need to create the csharp directory inside in Windows Explorer:

var path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
System.Diagnostics.Process.Start("file:///" + path);

Once you've got your "csharp" folder created save this in a file named MessageBox.cs in your csharp folder.

Now let's take our stack-based MessageBox for a spin. Start the REPL again and type the following:

csharp> var msgBox = new MessageBox();
csharp> msgBox.AddMessage("Message 1"); csharp> msgBox.AddMessage("Message 2"); csharp> msgBox.AddMessage("Message 3"); csharp> print(msgBox); Message 3 Message 2 Message 1 csharp> msgBox.RemoveMessage("Message 2"); csharp> print(msgBox); Message 3 Message 1

We can see that with this new MessageBox, messages are displayed in reverse chronological order.

We've seen three types of collections. There are many more to be found in the System.Collections and System.Collections.Generic namespaces.

Message Box Using Generic List

classMessageBox
{
private List<string>messages = newList<string>();
public intMessageCount { get { return messages.Count; } }
publicstring this [int i]
{
get { return messages [i]; }
set { messages[i] = value; }
}
publicvoidAddMessage(stringmessage)
{
messages.Add(message);
}
publicintFindMessage(stringmessage)
{
for (vari = 0; i < messages.Count(); i++)
{
if (message == messages[i])
{
return i;
}
}
return -1;
}
publicvoidRemoveMessage(stringmessage)
{
vari = FindMessage(message);
if (i > -1)
{
messages.RemoveAt(i);
}
}
publicoverridestringToString()
{
varsb = newSystem.Text.StringBuilder();
foreach(var s in messages)
{
sb.Append(s);
sb.Append('\n');
}
return sb.ToString();
}
}

Message Box Using Generic Stack

publicclassMessageBox
{
private Stack<string>messages = newStack<string>();
public intMessageCount { get { return messages.Count; } }
publicvoidAddMessage(stringmessage)
{
messages.Push(message);
}
publicintFindMessage(stringmessage)
{
for (vari = 0; i < messages.Count(); i++)
{
if (messages.ElementAt(i) == message)
{
return i;
}
}
return -1;
}
publicvoidRemoveMessage(stringmessage)
{
vartemp = newStack<string>();
vari = FindMessage(message);
if (i > -1)
{
for (varj = (messages.Count - 1); j >= 0; j--)
{
if (j != i )
{
varmsg = messages.ElementAt(j);
temp.Push(msg);
}
}
messages.Clear();
messages = temp;
}
}
publicoverridestringToString()
{
varsb = newSystem.Text.StringBuilder();
foreach(var s in messages)
{
sb.Append(s);
sb.Append('\n');
}
return sb.ToString();
}
}

Step 9: Wrapping Up

To recap, we've covered expressions, classes, methods, fields, properties, and containers. That should be more than enough to started on your programming journey. If you have any questions, don't hesitate to comment below.

Be the First to Share

    Recommendations

    • For the Home Contest

      For the Home Contest
    • Big and Small Contest

      Big and Small Contest
    • Make It Bridge

      Make It Bridge

    Comments