If you want to know how to develop software using C# and you have little to no experience programming you've come to the right place. This is an introductory course to C# and .NET programming that is the first in a series that will show you how to use these dynamic cross-platform development tools.
In this course, we set the stage in two ways. First, you get an overview of the basic elements of computer programming and fundamental issues that face software development, and how .NET addresses those issues. Secondly, we set up our software development tools on Windows and a non-Windows platform. To test our development environments we create and run a simple C# .NET program.
Learning Objectives
- Get a foundational understanding of computer programming and .NET
- Learn how to set up a development environment
- Learn how to run a simple C# .NET program
Intended Audience
This course is intended for anyone who wants to learn how to develop software using C#
Prerequisites
To get the most out of this course, you should have some basic understanding of programming, but it's not essential; this course can also be taken by complete beginners.
Let's create a .NET console application that will confirm our development environment is working correctly. We can also go through a C# program's basic structure, looking at key elements necessary to compile and build successfully. This exercise's primary purpose is to create a runnable program, but I will mention some new and unfamiliar concepts in that process. Don't worry if you don't fully understand some of them, as I will be addressing them later in detail.
In Windows, open the Visual Studio IDE from the start menu and select Create a new project. As this is the first time creating a new project, Visual Studio must download the different project types' templates. In the languages drop-down, we can see all the languages that Visual Studio supports, although each language is not supported for all platforms and project types.
Under platforms, we can see the major platforms are catered for. There is also a large selection of project or application types you can create and within each project type yet more options. However, for this first application, we will keep it simple and build a basic console executable.
In the spirit of cross-platform, I'll select the .NET Core option rather than .NET Framework, but either will do. Next, I'll give the project a name, which will also become the executable program's name. By default, the solution gets the same name. A solution can have multiple projects within it, and the solution name won't impact the name of the project.
Leave "Place solution and project in the same directory" unchecked so the project is created within its own folder that will have the same name as the project. For the target framework, I'll select .NET Core 3.1 long-term support and hit the create button.
There's a tradition stemming back to the first C program that the first app you make is called HelloWorld, and all it does is output the words "Hello World" to the Console, which is why this console program template defaults to what we see here. I'll just modify the text to be more in keeping with the 21st century.
Let's go over the anatomy of this project and program. On the right in the Solution Explorer is the HelloMars solution with the HelloMars project sitting just below it. There is only one code file called Program.cs in the project. Looking at Program.cs, the first line "using System" means that the program is using functionality from a namespace called System within .NET Core.
A namespace is a way of grouping functionality, the way a chapter in a book groups topics. The default namespace of this project is the project name. As a very rough guide, namespaces tend to correspond with libraries, not to be confused with book libraries, but dynamic link libraries – DLLs. The project references the System.dll that contains the System namespace.
When we compile and build the project, the compiler will know that it references functionality within system.dll. Within the actual code, we tell C# that we are using functionality within the system namespace. Namespaces are hierarchical, a little bit like a domain name where a dot separates each namespace element.
Below System, we can see System.Collections which is the collections namespace within System. Namespaces allow you to have functions with the same name but in different namespaces. Visual Studio will enable us to drill down into a .NET library with the Object Browser that is found under the View menu.
If I expand System.Console and click on Console; we can see all the methods of Console. We are outputting text with the WriteLine method that takes one string parameter or argument.
Within the HellowMars namespace, there is the keyword class followed by Program. C# is an object-oriented language where objects, that we'll learn about in the future, are defined as classes. What we have here is a program class. The class is called program by convention; you can call it anything.
The main thing, excuse the pun, is you must have a class with a static void method called Main. Main is called the entry point; it signifies the beginning of the program. The Main method takes one parameter called args which is an array of strings. If you don't know what an array is, don't worry, we'll discuss it later; just think of it as a list or group of text pieces, i.e. strings.
When you issue commands from the command prompt, like copy or delete a file, the file names or name are the arguments passed into the copy or delete program. That's what the args are here, and as you can see, we aren't using them. Finally, we have Console.WriteLine, which is doing all the work of the program.
Let's run the HelloMars program by clicking the green run arrow. At the bottom in the Output window, we get feedback and status on the compiling and build process; then the Console pops up with Hello Mars!. We are running this within the Visual Studio IDE, so all the stuff below Hello Mars is put there by the IDE.
If I were to run this program directly from Windows, the Console would just flash up for a fraction of a second and disappear. I'll add another console method that waits for user input underneath WriteLine. If I wait after typing Console, the IDE shows me Console's properties, with the black spanner, events with the lightning bolt, and methods or functions, with the purple cube.
I'll use ReadLine, which takes in a line of user input text from the Console, making the program wait for the enter key to be pressed, signaling the end of the line. Let's run that again to make sure there are no errors and sure enough works as expected.
To run the HelloMars program from Windows, I'll go to the HelloMars folder the drill down to bin, then debug and the folder for the version of .NET we are targeting – netcoreapp3.1 and double click the app.
Now I'll head over to the Ubuntu machine and create a similar application but in a slightly different way. Even though we don't have the project wizard of Visual Studio, we can still create applications from templates using the dotnet new command. Typing the command without parameters displays the templates available to us.
The templates column is the description; the short name is how we specify the template, language tells us which languages we can use with which templates. C# in the square brackets is the default. Tags describe the type of project. To create the same project as in Windows, the command is dotnet new Console; I don't have to specify lang, but I will and then "o" to set the output directory relative to the current path. That will also be the name of the project.
You can use an n or name parameter to name the project or give it a different name explicitly. I'll change to the hellomars directory and start VSCode with a dot that tells it to load the project files. The C# extension we previously installed knows VSCode isn't currently configured to build this project, and it's asking me if I want it configured, which I do, so I'll click yes.
A new folder .vscode containing launch.json and tasks.json has been added to the solution. Launch.json tells VSCode what to do when I run the application from within VSCode, as in debugging. The program line says run hellomars.dll from bin/debug/net5.0 directory.
Tasks.json is how VSCode tells dotnet to build, publish or watch, as in debug, hellomars.csproj. Opening program.cs, we can see that it's the same as our Windows version. This time I'm going to supply my name as an argument to the program and then add my name to the end of the output text to say Hello Mars from Hallam.
Remember I said args was an array of strings, and arrays are like lists or groups? You access an element of an array using a number known as an index, and the first element is element zero. The items of an array with two elements can be accessed with zero for the first element and one for the second, with the index enclosed in square brackets after the array name.
Here I'm getting the first arg item using index zero and assigning it to a variable called myName. A variable is just a container for storing a value, similar to mathematical formulas where you might assign the value 7 to X. In this program, I then add or append the myName variable to the literal string "Hello Mars! from ".
Note the space after the word "from." I don't want myName to be hard up against "from." Now, I'll compile and build the application by selecting Run Build Task from the Terminal menu and then choosing build. Remember, build is the first task in tasks.json.
Before I can run the program from within VSCode, I need to supply a name value. I can do that through the appropriately named args property of launch.json. I'll type some quoted text in there, as in my name. Save launch.json and select Run Without Debugging from the Run menu. There we go.
We can also build and run the project from the command line. Within the project directory, dotnet build compiles and builds the app. I can run the app within the context of dotnet with dotnet run. See what's happened here. There is a system.IndexOutOfRangeException. The program was trying to access the first element in the args array, but as I didn't supply a name or use launch.json to tell dotnet how to run the app, there were no arguments and no element zero to assign to the myName variable. If I re-run it with an argument, it will run correctly. I can also run the app directly from the command line as a stand-alone executable. Elon says Hello.
hellomars.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>
Program.cs
using System;
namespace HelloMars
{
class Program
{
static void Main(string[] args)
{
string myName = args[0];
Console.WriteLine("Hello Mars! from " + myName);
}
}
}
Windows (Visual Studio) - launch.json
{
"profiles": {
"HelloMars": {
"commandName": "Project",
"commandLineArgs": "Elon"
}
}
}
Ubuntu - launch.json
{
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/bin/Debug/net5.0/hellomars.dll",
"args": ["Elon"],
"cwd": "${workspaceFolder}",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}
Ubuntu - tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/hellomars.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/hellomars.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"${workspaceFolder}/hellomars.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}
Hallam is a software architect with over 20 years experience across a wide range of industries. He began his software career as a Delphi/Interbase disciple but changed his allegiance to Microsoft with its deep and broad ecosystem. While Hallam has designed and crafted custom software utilizing web, mobile and desktop technologies, good quality reliable data is the key to a successful solution. The challenge of quickly turning data into useful information for digestion by humans and machines has led Hallam to specialize in database design and process automation. Showing customers how leverage new technology to change and improve their business processes is one of the key drivers keeping Hallam coming back to the keyboard.