First Steps with Mono

From LXF Wiki

Want to learn to program? Here's your chance - Paul Hudson kicks off a new series on Mono, .NET and C#...

Programming rocks my world, and I want it to rock your world too. If you've never programmed before, this series is for you: I'm going to teach you everything you need to know to become a professional programmer, and I'm going to do my best to make it fun and interesting for you.

For this series we're going to be using Mono, which is an open source programming platform based on Microsoft's .NET. If you've never heard of any of this, don't worry - we'll get onto the theory later. Right now, I want to get you started programming straight away, so without further ado...

Table of contents

Getting started

So that we're all singing from the same songsheet, I'll be using Fedora Core 6 as the base distro for this tutorial. It ships with everything you need to learn to program with Mono, and if you and I are using the same version then you can follow my instructions verbatim and should have no problems. If you're not using Fedora Core 6, please see the box "Good news for Fedora haters".

If you are using FC6, much of your work is already done for you, but in order to get the most of out of this tutorial you need to add a few extra packages. Choose Add/Remove Software from the Applications menu, then when the window appears change to the List view. You need to make sure the following list of packages are selected: avahi-sharp, dbus-sharp, evolution-sharp, gecko-sharp2, gmime-sharp, gnome-sharp, gsf-sharp, gtk-sharp2, gtk-sharp2-devel, gtk-sharp2-doc, gtksourceview-sharp, gtksourceview-sharp-devel, ipod-sharp, mono-core, mono-data, mono-data-sqlite, mono-debugger, mono-devel, monodevelop, monodoc, mono-extras, mono-jscript, mono-locale-extras, mono-nunit, mono-web and mono-winforms. Some of these will be installed already, but most will need to be selected by you. Once you're ready, click Apply to have Fedora download the latest versions of these packages and install them - it may take a few minutes.

What we've just done is set up Mono so you can access many different parts of your system. We will probably only use a small subset of the packages in this tutorial series, but having them installed means you can branch out to whatever interests you. Included in that list is evolution-sharp, for accessing Evolution's email and calendar information; gecko-sharp2, for embedding a Gecko/Mozilla web browser into your applications; and ipod-sharp, for writing programs to access your iPod.

The "sharp" suffix signifies that the library is for .NET, which means that "foo-sharp" enables the "foo" library to be used with Mono. "Sharp" is used because the most popular .NET programming language is called C#, and # is of course the musical notation pronounced "sharp".

The most important thing we've just installed is MonoDevelop, which is the primary development environment for creating applications with Mono. Yes, you can type your programs into a plain old text editor if you want, but why bother - MonoDevelop has so many helpful tools that you're just expending energy needlessly by not using it.

Hopefully all those packages will have downloaded and installed by now, and Fedora should have added a new menu item to let us run MonoDevelop: select Applications then Programming, and click the MonoDevelop shortcut.

Good news for Fedora haters

Okay, so you probably don't /hate/ Fedora - I'm sure you've chosen [Ubuntu/SUSE/Mandriva/Gentoo/MikeOS] for very good reasons. But all the steps in this tutorial series are tested using Fedora Core 6: if you're not using it, I can't guarantee it will work on your system.

Most distros ship with Mono, or at least make it an option inside their package managers. The package names will be very similar on other distros, although Debian/Ubuntu tends to use "-dev" rather than "-devel" for development packages.

FC6 ships with Mono 1.1.17 and MonoDevelop 0.12. If you have older versions and are experiencing problems, you can try downloading the "Linux Installer for x86" binary from http://www.mono-project.com/Downloads. This puts Mono into a contained subdirectory on your system, and if you set that as your primary environment you should be able to follow the tutorial.

Note: if you aren't using Fedora Core 6 and encounter problems following this tutorial, please don't hesitate to ask someone else about it first.


It's all about integration

MonoDevelop is the Integrated Development Environment (IDE) for Mono coders. The whole point of an IDE is that it brings together all the tools you need to program, so that you can get more work done with less hassle. We'll be doing all our coding, compiling and debugging in MonoDevelop, because it's a very powerful IDE to use. Of course, the flipside to "powerful" is "hard to learn" - MonoDevelop might look a bit overwhelming at first, but you can ignore most of it for the time being. I'll be explaining things as and when you need to know them.

Let's get started with our first project: Hello World. If you've followed any prior programming tutorials, you'll already have an idea that the goal of this project is to make Mono print "Hello World!" out to the screen. It's not a hard task, but it gives a solid base from which we can develop other programs.

To get started, go to the File menu and choose New Project. When the new window appears, choose C# from the left pane, then Console Project from the right. Under "Location", give your project the name HelloWorld. Place it in a directory where you'll be able to save all your work as you go - I tend to use a directory called "sandbox", but it's your call. Make sure and deselect the checkbox "Create separate Solution subdirectory". That's it: click New to have MonoDevelop create our skeleton project.

After a few seconds of your hard disk buzzing busily, MonoDevelop will spring to life: on the left you'll see some files have appeared (including Main.cs and AssemblyInfo.cs), and on the right you'll see some scary code appear. Ignore it all for now: press F5. This tells MonoDevelop that you want to run your program, so it will quickly compile the code and run it. Beneath the pane containing the code it will say "Application Output", and once your code has run, it will say "Hello World!". Mission accomplished - and without even having to write any code!


Breaking it down

Okay, so perhaps that was cheating: MonoDevelop's basic console project comes with the Hello World text already written, so we haven't actually done any coding yet. But we do have some code that we can edit, so let's start there.

MonoDevelop generated 13 lines of code for us, of which only five contain anything special. Here's how it should look:

// project created on 11/15/2006 at 1:59 PM
using System;

namespace HelloWorld
{
   class MainClass
   {
      public static void Main(string[] args)
      {
         Console.WriteLine("Hello World!");
      }
   }
}

The first line starts with two slashes, //, which marks that whole line as a comment. When Mono converts this source code into something that can be executed by the computer (this is all "compiling" really means), it discards all these comments. You can have a thousand lines of comments in your program, and it won't affect the speed one bit, because they just get ignored by the compiler. Comments are useful for us, though, because we can use them to explain how something works or why we've written our program a certain way. This comment was automatically inserted by MonoDevelop to mark when the project was created - it's pretty useless, so you can delete that whole line if you want to.

The next two lines deal with our first piece of theory: namespaces. .NET is big. Very big. It has libraries to deal with XML, graphics, networking, user interfaces, files, security and much more. There are also thousands of libraries that other people have written, which you can download and plug into your own code. Each of these libraries defines their own names for things, which meant that if all these libraries were loaded simultaneously it would seriously restrict the names you can use in your own code. C# works around this problem with namespaces: each library gets filed somewhere under the .NET hierarchy, and you need to specifically load it in.

"System" is a namespace, which incorporates all the most basic things that .NET needs, including the ability to write our "Hello World" message to the console. If we didn't write "using System;", the line of code "Console.WriteLine" wouldn't work, because Mono wouldn't know where to find "Console". Instead, we would have to be specific that we meant the Console in the System library, by changing the line to this: "System.Console.WriteLine". So, saying "using System;" is a shortcut - we write one line of code now to save typing later. Note that this line ends with a semi-colon, which is there to tell Mono that the line has ended.

After the "using" line we define our own namespace: HelloWorld. Again, this is to make sure our own code doesn't clash with code that other people have written. If you have very unusual names for things in your code, you can do without a namespace, but there's no harm having one so we recommend just leaving this in.

Immediately after our namespace declaration is a line containing an opening brace (curly brackets, "{") all by itself. In fact, there are lots of these { and } throughout the code, so you're probably wondering what they do. In C#, braces are used to define where things start and end, where "things" are technically referred to as "code blocks". In the code above, the opening brace immediately after our HelloWorld namespace line says "the namespace starts here." At the end of the code, there's a closing brace, which means "the HelloWorld namespace ends here." Everything between the open and close brace is therefore part of the HelloWorld namespace.

There are four other opening and closing braces inbetween, each marking the start and end of things. The compiler doesn't really need the braces to be neatly spaced and aligned like this, but it makes it easier for us to read - we can just follow the columns down to see where the current code block ends. Notice that no semi-colon is needed after lines with opening or closing braces, because these don't actually do anything in the code, they just represent structure.

Inside our namespace is one class, called MainClass by MonoDevelop because it doesn't know what we want to program. C# uses something called "object-oriented programming", which means you define entities in your code (known as "classes"), then you create instances of those entities when you want to do things (known as "objects"). For example, if you're writing a racing game, you'll want to have a class called "Car", then create ten objects of that class so you can place the cars on your track. Each object has its own data, which means each car can have its own position and speed.

Our class, MainClass, has no data of its own, but it does have a method called Main. Methods are actions that you want objects to perform when you ask them to. Going back to our car example, you might want the methods Accelerate, Decelerate, ChangeGear and Crash. ChangeGear is all one word because method names can't include spaces (although they can include underscores, if you want). These methods are used to make the object do something - in the case of Accelerate, it would increase the speed of the car.

The Main method looks pretty ugly, because it's preceded by three special keywords: public, static and void. Ignore these for now. It also has "string[] args" after the name "Main" and inside some brackets, which also makes it look more complicated than it is. Again, ignore this for now; we'll get onto it soon. Hopefully, though, you noticed that there's an opening brace on the next line, telling Mono where the method starts.

The last line we need to look at (the rest are all closing braces) is the Console.WriteLine line. Console is an object representing the console that ran our program, and is where our output is sent. WriteLine is a method of that object, which prints text out to the console. To call WriteLine, we just need to send it some text inside quotes and brackets, like this:

Console.WriteLine("Hello World!");

The brackets, ( and ), are used to tell Mono we're calling the WriteLine method; without them it will get confused.

It took a fair amount of explaining just to go over 13 lines of code, but you've already sucked up a good amount of theory and can try impressing your friends with your knowledge of namespaces. Or, if you're feeling eager, you can push ahead...

Doing more with WriteLine()

Our "Hello World!" text is pretty dull, but you can put any text you like inside the quote marks and our program will print it out. Remember, press F5 to have MonoDevelop rebuild and run your new code. One exception to the "put any text you like" rule is if you want to do something like this:

Console.WriteLine("And then Nick said, "I suck!", and really meant it.");

If you try typing that into MonoDevelop, you'll see the problem because MonoDevelop automatically highlights text data in magenta. What you'll see is "And then Nick said," all in magenta, followed by "I suck!" in black, then ", and really meant it." in magenta again. Consider this: if Mono uses quote marks to mark the start and end of text, how can it tell the difference between a quote mark you want to print out, and a quote mark you're using to mark the end of your text? The answer is that it can't, and so it thinks you want to print out the text "And then Nick said,". The next part, "I suck!", is treated as C# code, which makes baby Jesus cry.

If you want to include quotes in your text, you tell Mono that you want to print them out, and that they shouldn't be used to end the text. In C#, this is done with a special escape character: \. An escape character tells C# that the letter after it is special, and should be treated as such. In the case of making it print quotes, you need to put the escape character before the quote, like this:

Console.WriteLine("And then Nick said, \"I suck!\", and really meant it.");

Other escape characters are \n, meaning "start a new line"; and "\\", meaning "print a backslash" (that is, "print a backslash rather than treating the \ as an escape character). The \n escape character isn't really needed, because the WriteLine method automatically adds a new line return to the end of every line it prints out. For example, if we were to change the code to this:

Console.WriteLine("He thrusts his fists");
Console.WriteLine("Against the posts");
Console.WriteLine("And still insists");
Console.WriteLine("He sees the ghosts");

That would print out a piece of text per line. If you wanted to control the lines yourself, you could just use Console.Write, like this:

Console.Write("He thrusts his fists\n");
Console.Write("Against the posts\n");
Console.Write("And still insists\n");
Console.Write("He sees the ghosts\n");


Parameters 101

Let's go back and take a look at the "string[] args" part of our Main method. Every method definition has to end with an opening bracket, (, and a closing bracket, ). Inbetween those two brackets are where we can specify what data - if any - we want to be able to send to the method. Using our car example, how would the ChangeGear method know what gear to change to? The answer is by allowing it to receive the gear number. This is where method parameters come in: we can define all the data our method can receive, and C# will make sure that people calling that method will pass in the right data. Each piece of data is called a parameter, and needs to be given a data type (is it a number? Is it text? Etc) and a name that will be assigned to it inside our method.

In the case of "string[] args", our code is stating three things: the Main method must be passed precisely one parameter. That parameter will be called "args" inside the Main method. Finally - and most confusingly - it will be an array of strings (the array part is denoted with the symbol [], which is an opening and closing square bracket next to each other). A "string" is just a fancy term for what we've been calling text - it's any sequence of characters starting and ending with a double quote symbol. So, "hello world" is a string. An array is a group of objects that are the same type, which means that our "args" parameter might be empty, it might contain one string, it might contain one hundred strings - it's all down to what gets passed in when the method is called.

If this is your first time programming, you may not have spotted the problem with what I've just said, so I'll outline it for you: where are we actually calling our Main method? The answer is: we're not! And yet "Hello World!" does get printed out. What's happening here is that "Main" is a special name for a method. When our program runs, Mono automatically looks for and calls the Main method, and sends it any parameters that were used on the command line. We're all used to typing "cd /usr/bin" to change directories, and it's that "/usr/bin" that is the parameter to the "cd" command. When Mono calls our Main method, it sends those parameters to us in this "args" string array, which means we're poised to make our first useful program: taking user input and doing something with it.

Create a New Project (File > New Project > C# > Console Project > Deselect the silly checkbox) and name it "ParamPrint". It's going to take accept one parameter on the command line, and print it out alongside our "Hello World!" message.

With the new code that MonoDevelop generates, change the Console.WriteLine line to this:

Console.WriteLine("Hello World: " + args[0]);

Because the "args" variable is an array of strings, we need to tell C# which item of the array we want to print. Array items are ordered numerically, with 0 being the first item, 1 being the second, and so on. So, to print the first parameter that was given to us, we need to use "args[0]". The + is in there to link our "Hello World!" string with the first parameter, to tell C# we want them to be combined together.

If you've gone ahead and hit F5, you'll see something rather grim: rather than printing out a message as it has done previously, MonoDevelop's application output pane will print a rather scary along the lines of "Unhandled exception: System.IndexOutOfRangeException". This has happened because "args[0]" refers to the first parameter being passed into our program, but we're not passing /any/ parameters to the program - MonoDevelop just runs it.

The solution to this is to open up a terminal window and send our program some parameters by hand. From the Applications menu, choose Accessories then Terminal. Now change directory to where you created your project (I used /home/paul/sandbox/ParamPrint). Inside there you'll see a "bin" directory, which is where MonoDevelop puts all the programs it has compiled for this project. Inside the "bin" directory you'll see "Debug", which is where MonoDevelop stores binaries created while we're busy coding. Inside /that/ directory will be your program: ParamPrint.exe. To run it, use this command:

mono ParamPrint.exe

You'll see the same error message printed out to your console, but now you can try adding a parameter:

mono ParamPrint.exe LXF

That will print out the message "Hello World: LXF". But if you try this:

mono ParamPrint.exe Linux Format

That will print out the message "Hello World: Linux". The full message isn't printed because each parameter is sent as its own string in the args array, and parameters are separated by spaces. In the example above, "Linux" would be args[0] and "Format" would be args[1]. If you want to print the full message, change the command to this:

mono ParamPrint.exe "Linux Format"

The quotes tell Bash, your shell program, that "Linux Format" is just one parameter.


Why Mono uses .exe

When Mono creates executable files from your C# code, it uses the file extension ".exe", which we all recognise as the Windows standard executable format. In fact, if you try executing your programs from the command line without using "mono" in front of them, it may not work, because Linux won't know what to do with it.

The reason Mono uses .exe for its files is for cross-platform compatibility. If you take your program, ParamPrint.exe, and copy it onto a Windows machine, it will run natively and print out the same message when you pass a parameter to it. This is because Mono is based on Microsoft .NET, which is installed on most Windows computers. No recompilation is required, no source code editing is required, and actually no one can tell what platform the program was compiled on - and it doesn't matter, it just works.


So far so good

This issue you've had a brief introduction to MonoDevelop, you've learnt what namespaces, parameters and methods are, how C# code is structured, and how to print messages to the screen. You have also had a quick rundown of object-orientated programming, which is quite a complicated topic when you get stuck into it. But most importantly, you've made a program that can accept user input and print it out, which isn't bad going for a four-page introduction!

Next issue we're going to start tackling real problems, which is where the fun begins.


Top tips

- If you want MonoDevelop to compile your code without running it (which you will once you start needing to provide arguments), use the F8 key.

- Both the Build (F8) and Build and Run (F5) shortcuts are available from the Project menu.

- Once you have a finished program you want to distribute, click the box that says "Debug" above your code, and change it to Release. This optimises your program so it runs faster, and should also generate a smaller executable file.

- The left pane of MonoDevelop is where it shows your files, but along the bottom of that pane you can see you're actually looking at the Solution tab. If you change this tab to Help, you'll be able to browse the Mono documentation while you're coding.