Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Programming Linux With .NET Core 3: Chapter 3

2.80/5 (4 votes)
16 Aug 2020CPOL24 min read 6.7K  
Learn to program Linux via .NET Core 3 and Visual Studio code: Beginning Console Apps
Learn more about Data Types and Basic Control structures as we learn about .NET Core compiler errors, the Main() function, basic data types and how to obtain input from the terminal command line.

If you're just joining, you can find the beginning of this book at:

Chapter 3 - Data Types and Basic Control Structures

Here are some of the things you’ll learn about in this chapter:

  • Compiler errors and warnings
  • The Main() function signature requirements
  • Data types (int, string, float)
  • Arrays of data types
  • How to obtain input from the terminal command line

Using Code on the Way to Learning to Code

Some books teach you all about syntax and data types and all that to teach you the language, but in this book I show you how to use code and then we talk about why it works. Along the way, you will learn syntax and data types and the rest, but you will also see the code in action.

Let’s create another new project. Open up your terminal and go to your target directory and run the following command:

$ dotnet new console -o SuperConsole

After you do that, open up Visual Studio Code (VSC) and open the project folder.

When you open the project folder, VSC will notice that you are opening up a project that is based upon C# and it will pop up a small window in the bottom right corner.

Image 1

That’s the OmniSharp plugin (or extension) we previously installed. It looked like the following:

Image 2

Since we want to activate the plugin to help us with this project, you can go ahead and click the [Yes] button and it will be activated for this project.

Once you do that, go back and click the File navigator icon on the upper left and then select the Program.cs file.

Image 3

Of course, you see that this project once again has that same one line of code which will output Hello World!

Go ahead and delete that line of code and then we’ll talk about the main function a bit more.

Rename the Main Function

After you remove the line of code, go ahead and rename the Main function so that it is named MainX like I’ve done in the following code snippet. You can copy it if you like:

C#
static void MainX(string[] args)
{

}

Save the changes and go back to your terminal window and attempt to run the program.

$ dotnet run

Learning to Deal with Errors

When you do, you will get an error.

Image 4

Take Each Bit, One Step at a Time

When you see all of that red text, it can be a bit overwhelming. That’s why it is best to really examine it and see what sense you can make of it.

Error Codes

It says CSC : error CS5001. That is the CSC (C Sharp Compiler) -- the thing that looks at your code and turns it into a program -- telling you that an error that it identifies as CS5001 has occurred.

The specific error code is provided for easier lookup. At first, the error code doesn’t mean much to us but if we were to search it on DuckDuckGo.com (my search engine of choice), then you can get more details.

Here’s the Official Microsoft documentation on it:

Image 5

Just as I said in the previous chapter, you are required to have a Main method in your program as an entry point (starting point) for your program.

What is a Function Signature?

However, you could have the correct name on the Main method, but an incorrect signature.

Let’s go back and change the MainX back to Main. But this time, remove the static word from the beginning of the function. It will now look like the following:

C#
void Main(string[] args)
{

}

After you make the change, save the file and go back to your terminal and try running it again.

$ dotnet run

You will get the same error, even though the error has occurred for two slightly different reasons:

Image 6

The compiler can’t always differentiate and give you a specific reason the error occurred and you’re going to have to become accustomed to looking very closely at why things may not be working.

In this case, the Main function signature is not quite correct.

There’s that term again: function signature. But what is a function signature?

A function signature is made up of four things:

  1. Function name
  2. Function parameter type(s)
  3. Function return type
  4. Function modifiers

To determine if you have a correct Main function, you have to make sure that all of those four things match the allowed signatures that are defined by the C# Language.

In this case, we have the correct Function name. It is Main and it has to be uppercase first letter with the rest lowercase.

We have the correct parameter types. In this case, the only parameter is an array of type String which looks like:

C#
string args[ ]

Next, we have the proper return type which is void.

The keyword void means the function doesn’t actually return anything.

Finally, the function signature has to have the same modifier also. In this case, the function modifier static is missing. Note: The public part of the method is also a part of the modifier. We will learn more about these modifiers later but don’t worry about that for now.

Since the keyword static is missing from the function signature, the CSC (C# Compiler) cannot find a valid Main method.

Overloaded Functions: Numerous Acceptable Function Signatures

If you do a bit more digging in the Microsoft documentation on the Main function, you will find a list of valid Main function signatures. The Main function is said to be overloaded because it recognizes multiple signatures for the same function name. You’ll learn more about overloaded functions as we continue through this book.

Image 7

As you can see, all valid Main methods have the public static modifier. If either of those modifier keywords are removed or changed, the method signature will be incorrect.

We won’t talk about all those other signatures, but now you understand that there is a well-defined requirement for how the Main method of valid console application has to be created.

Fix Your Main Method

Go ahead and add the static keyword back to your method again so we don’t keep getting an error.

C#
public static void Main(string)
{

}

Also, it is a good idea to go ahead and build and run this program to make sure everything is set up properly again.

$ dotnet run

Of course, the program doesn’t output anything right now so nothing really happens when it runs.

Now, let’s start examining the Console library.

More about string args[]

The one parameter that Main has that the template project provides is the string args[].

That is an array of strings. That means that this variable named args can contain from 0 to many strings.

What Does Args Represent?

The name args means arguments. Some devs refer to the parameters that a function takes as its arguments. The parameters or arguments is the list of variables that the function will take in and work on or use.

In this case, since it is the main method, these are values that the user can pass to the program from the terminal when they start the application.

A good example of this is when we change directory with the cd command and we give it a path or directory name.

$ cd ~

That takes us to the user home directory. The ~ is the argument or parameter to the cd command.

Another example is the ls command.

If we want it to list the files and directories each on a separate line as a list, then we provide it with the -l parameter.

Here’s an example of ls by itself and ls -l.

Image 8

What Can Args[ ] Do?

Using the args array, we can get the user’s input that she typed on the command-line when she starts the program. It’s actually kind of cool.

Let’s start adding some code. We want to get the number of parameters that the user has provided. We can do that by checking a property of the args variable called Length.

OmniSharp Can Help

Because we have installed and activated the OmniSharp extension in VSC, we will get Intellisense code completion. That means when we begin to type code, the extension will actually run a program to look inside the .NET libraries that we are working with and get some information that may help us.

Here’s an example.

When you type Console.WriteLine, the extension will look inside the library for comments and parameter names and more and then it will pop up the information inside VSC.

Image 9

You can see that it started trying to help me with Console.WriteLine but it stopped on one overload of the Console.WriteLine method. See the up/down arrows that say 02/18 between them? That is stating that this is the second overload of eighteen Console.WriteLine methods. There are many Console.WriteLine methods that take many different parameters.

Also, notice that I took a snapshot right after I typed args.L and the OmniSharp extension is telling me there are two possible choices (Length and LongLength). I know I want Length so I can click the item in the list or hit <ENTER> and the highlighted choice will auto-type in VSC.

Here’s what my final code looks like:

C#
public static void Main(string[] args)
{
    Console.WriteLine(args.Length);
}

Once you have that code, go ahead save the file and switch back to your terminal window and run it.

$ dotnet run

Image 10

You can see that it reports 0 parameters were sent in by the user. That makes sense because we didn’t add any.

Try it again, but this time, add two parameters. Each parameter is simply separated by a space.

$ dotnet run first second

Image 11

You can see that it now reports that two parameters were provided.

Using String Interpolation

Our output isn’t very nice since it just outputs the number. Let’s learn how to use the String interpolator which will allow us to create a nice output string that contains the variable values along with a descriptive string.

Let’s change our current code that is writing the line to the output so that it now looks like the following:

C#
Console.WriteLine($"There are {args.Length} args.");

A normal string literal is any characters which are wrapped between two double quotes like the following:

"This is a string"
"I am also a string"
"Anything between double quotes"

However, in the case where we want to include a variable value inside the string and we want the value to be output, we use the string interpolator $ immediately before the first double quote.

That tells the compiler that any time it finds items between the double quotes which are also between curly brackets that it should retrieve the value from the item and replace it in the string so the value will show up in the string instead of the variable name. In our case, the string interpolator will get the value of args.Length and replace it inside the string.

Your code will now look like the following. Once you save it, go ahead and run it.

C#
public static void Main(string[] args)
{
    Console.WriteLine($"There are {args.Length} args.");
}

Image 12

That output is much nicer and more informative to the user and now you know how to add values to your output in the middle of a nice formatted string. We’ll keep using string interpolation throughout so keep an eye out for it.

If you want to read more about interpolation right now, check out this link.

Let’s Use the Parameter Values

Well, this is very nice knowing how many parameters were sent in, but it would be a lot nicer if we could use the actual parameters somehow.

Only Trying to Use args When There Is At Least One

It would be best to only try to use the args when there is one or more.

To do that, we could use an if statement. An if statement allows us to control our program by only doing something if something is true.

For example, if args.Length is greater than zero, then let’s do something.

To write that as code, you have to use the proper C# syntax (language rules) of an if statement.

It looks like the following:

if (predicate){
     // execute functionality between curly { } braces.
}

The predicate is the statement which is evaluated to determine if it equals true.

In our case, we want to determine if args.Length > 0 so we write the code like the following:

C#
if (args.Length > 0){
    Console.WriteLine($"The first parameter is : {args[0]}");
}

The entire code listing looks like the following now:

Image 13

Make the changes and save your file and let’s run the program again but this time with no parameters.

$ dotnet run<span id="cke_bm_138E" style="display: none;"> </span>

Image 14

Since there are no args passed in and the code checks the args.Length > 0 then the code inside the new if statement doesn’t run.

Now try it again with 1 or more arguments.

$ dotnet run one two

Image 15

You can see that the code inside the if statement does run now.

Let’s talk more about what the code actually does and talk about this special thing called an array.

String Array

First of all, C# keeps track of the type of variables that we are using. That means we have to tell it the type of data that a variable will be holding.

C# is considered a strongly-typed language which means it always wants to know the type of the data in every variable that we create.

This is a protection to the developer so she doesn’t accidentally store the wrong type in a variable and then try to do the wrong type of operations on the variable and get an odd result.

Here’s what I mean.

Declaring Variable Types

When we create a variable for use in our programs, we need to do two things:

  1. Name the variable
  2. Define the type of data that will be stored in the variable

To set up a new string variable for use, we can simply do the following:

C#
string firstName;
string lastName;

Variable Name

Now the program will understand that you have two different buckets for placing strings in.

The name is a way of providing the developer with a way to access the variable so she can store a value in it. That may seem kind of obvious but if you didn’t have a name to use throughout the program then you couldn’t refer to the variable again.

Name is a Memory Address

Keep in mind that the program itself doesn’t really care about the name. Down inside the program itself the name is really a memory address.

In C#, you can easily do the following:

C#
string firstName = "Ted"

However, C# turns that into something like:

0x00FC53 ["Ted"]

That’s a memory address and the string is stored there. Obviously, knowing that firstName holds a value that indicates the first name of a person is a lot easier than memorizing random hexadecimal memory values.

Back in the day, programmers would look at memory addresses because they didn’t have these high level languages that allowed us to use variable names instead of memory addresses. That’s what C# does for you. It is a high level language that allows you to use natural language (English, Spanish, etc.) to refer to things in your program instead of some kind of machine language where you have to keep track of memory addresses yourself.

This is one of the first advantages of learning a high level language like C#.

Compiler & Building a Program

This also reveals why you have to build your program before you can run it. The CSC (C# Compiler) runs and examines the high level language C# and turns it into a language that the CPU (Central Processing Unit) understands. Note: There is really another intermediate step from C# to the processor but I’m simplifying things here.

Let’s go add those two variable name declarations to our program.

I simply added them at the top of our Main() function so we could use them if we wanted.

Image 16

Notice that at this point they are only declared for use since they have a name and a type, but they have no value.

Add those, save the file and run the program again.

C#
$ dot net run one two 

When you run the program, you’ll get two compiler warnings.

Image 17

The compiler tells you that you’ve declared them but you’ve never used them. This does not stop your program from running. If it would’ve been an error, the compiler would’ve stopped you. But since it is only a warning, it allows your program to run.

The warning is just a sanity check because the compiler is wondering why you added things to your program that were never used. Removing extraneous / unused code is a best practice so it lets you know about these types of things.

Using a String Variable

To use the string variable, all we have to do is set it to a value.

We can use a string literal (any characters wrapped in double quote symbols) to initialize the variable.

You can initialize the variable on the same line where you declared it, or you can initialize it later.

I altered the code so it looks like the following now:

C#
string firstName = "Ted";
string lastName;
lastName = "Lastonian";

If you run again, you will see that the compiler gives us a warning again, but it is slightly different.

Image 18

It is now warning us that the variables do have a value assigned to them but the value is never used. The compiler can seem like a cranky old man at times spouting warnings about everything.

Understanding Errors & Warnings: Better Programmer

However, the compiler is really just trying to keep things working and the more you get accustomed to what the warnings and errors mean, the better programmer you will be.

This ability may be one of the major things that separates a professional developer from an amateur. Problems occur in the real-world software all the time and the better you get at dealing with them, the more valuable your skills will become.

The more you read the cryptic error and warning messages from the compiler, the better and faster you get at determining what the problem is.

Let’s use both of the variables to write some output and then you’ll see the compiler stops complaining.

We’ll add this one line:

C#
Console.WriteLine($"{firstName} {lastName} was here.");

The entire source listing looks like:

Image 19

You can see we are using the string interpolator to create our string. We wrap each variable name that we want output in its own set of curly braces. Now when we run, we see the following:

Image 20

Other Data Types

Now we know that C# supports the string type, but what are some of the other types it allows us to use.

Here are some of the other data types:

C#
int counter;        // integer
double patientTempCelsius; decimal value, floating point (double precision)
bool isDoorClosed;  // boolean - true or false value
char middleInitial; // single character value

Comment Symbol //

Notice that at the end of each line, I’ve added two slashes then a comment about the data type.

The two slashes tell the compiler to ignore the rest of the line.

That means that everything after the two slashes becomes just a comment to other developers (or yourself at a later date). The compiler will completely ignore everything after those two slashes so even if the stuff would cause errors, the compiler ignores it as if those characters are not even there.

The ability to comment code is also nice when you want to remove lines of your code but leave them in the source file because you may use them later.

If you don’t want the line to be considered in the program, simply add two slashes to the beginning of the line.

C#
// int x = 50; 
// Console.WriteLine("This will never appear in the output.");

Developers Choose Data Types

As we design our programs to do the automation work we want them to do, we choose data types.

We choose the data types guided by what will be stored in them and what types of operations we need to do on the data.

If we need to display information to the user, we choose a string type.

If we need to count some number of things in our program, then we will choose an integer type.

If we need to store the price of an item so we can later do calculations on the number we need a floating point type (to store the decimal associated with dollars and cents - 1.99).

For example, let’s say we have something like the following:

C#
float unitPrice = 2.04f;  // notice the f after the numbers
int numberUnitsAvailable = 282;
double totalCost = unitPrice * numberUnitsAvailable;
Console.WriteLine($"Total cost is {totalCost}");

If you run that code, you’ll see something like the following:

Image 21

The odd f that follows the literal value (2.04) we added to unitPrice is a way to ensure that the compiler sees the value as a floating point value. Because the decimal operator (.) is used in other places, the compiler may believe you are using it a different way so we explicitly tell it that we are using 2.04 as a float value by providing that f.

Choosing Data Types by Max Value

However, we also choose data types by thinking about the constraints of the size that the values will grow to.

For example, a regular integer declared as int above uses a max of 4 bytes of memory.

Four bytes is thirty-two bits. A four byte or thirty two bit number can only hold a signed value in the range of:

-2,147,483,648 to 2,147,483,647

If you need to store values larger than 2,147,483,647 ( a little over 2 billion), then you’ll need to choose the next size integer which is called a long.

You declare a long just as you do the int.

C#
long federalDeficitDollars;

A long is 8 bytes (64 bits) and can hold a much larger (and smaller) number:

-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

That’s over 9 quintillion.

We’ll discuss more about data types as we go, but this is enough to give you a general idea.

Multiple Values in One Variable: Array

So far, we have been able to store one value in one variable.

We can store one integer in one integer variable. One string in one string variable.

Now that you have a better idea about data types, let’s talk about a data structure which allows you to store multiple values in one variable.

An array is a list of elements. The element types will all be the same.

There are times when you want to store a list of values together which will make it easier to iterate over the list in your programs.

Sometimes, it’s just more convenient to keep it all together under one name since all of the elements are the same data type and are similar values.

The string arg[] array is a perfect example.

That item is a list of items that the user has sent into the program. They are all similar in that they are all provided on the program’s command line.

You can make arrays from any of the data types that you’ve learned about so far (and many others).

All you do to declare an array is add the open / closing square brackets after the type and before the name of the variable.

C#
int [] studentsPerClassCount;
float [] distances;
string [] lastNames;

The compiler will understand that you are creating arrays of those types and you will reference them by the one name. Whenever you see square brackets, you should start thinking array.

That’s because you’ll use the square brackets to [] get the values out of the array also.

First, let’s learn how to initialize the values in an array.

To initialize, you simply create a list that is wrapped inside a set of curly brackets and where each value is separated by commas.

C#
int [] studentsPerClassCount = {30,23,17,31,34,12,58,32};
float [] distances = {3.23f, 17.389f, 38.37f, 1.23f};
string [] lastNames = {"smith", "jones", "jackson"}

Once you have your arrays initialized with values, how do you get the values out for use?

Arrays are Indexed

Each array is just a list of data of the same type.

Each array item is stored at a specific array index within the array.

The syntax for obtaining a value at a specific index looks like the following:

<arrayVariableName>[indexValue]

You provide the array variable name followed by an open square bracket [ , the index value of the item you want to get and then a closing square bracket ].

Arrays are Base 0 Indexed

That means that the first item in the array is zero. The indices are based upon the starting number of 0, not 1.

So to get the first value in the distances array above, you would simply do the following:

C#
Console.WriteLine(distances[0]);

Here’s the entire code listing at this point.

Image 22

Here’s the output you receive when you run the program.

Image 23

The last output of 3.23 is the first element of the distances array.

I taught you all of this about arrays just to get you to this next section. It is the really cool part about arrays.

Iterate Over Entire Array

You can iterate over the entire array. That means you can run code that will get each element of the array and do some work on it.

The C# Language and C# runtime have some smarts about them. They know all about your code. They know what your data types are but they also understand that special types can do special operations.

C# Language Control Structures

The C# Language also provides some control structures (if statements, foreach statements -- which we are about to learn about, while statements, and more).

These control structures allow you to actually control how your code runs.

You already saw an example of a control structure with the simple if statement we worked on earlier. It controls whether or not the code inside the body of the if statement will run or not.

Now we’ll see a new control structure called the foreach statmenent.

Foreach Control Statement

The foreach control statement works with special types like the array type (known as a collection) to determine whether or not code will run.

In our case, we only want code to run if our array contains one or more elements.

And, if our array contains more than one element, we want the control structure to run numerous times. However, we also don’t want it to attempt to access an index that is higher than the number of elements in our array. That would cause an error in our code.

For example, if we tried to obtain the value of distances[30], we would get an error.

The distances array only contains 4 elements so distances[30] is not defined.

The foreach control statement checks all of this and ensures that it will not go beyond the bounds of the array.

Here’s what it will look like to iterate through exactly the number of elements that are in your target array.

C#
foreach (float f in distances){
     Console.WriteLine($"value : {f}");
}

We use the name of our control structure (foreach) and then inside the open and closing parentheses (), we define a new float named f for each of the float variables which exists in the distances array.

Here’s a generic version of the foreach structure:

C#
foreach (type <variableName> in <array>){
}

If you were going to iterate through the array of strings, you’d do the following:

C#
foreach (string LName in lastNames){
     Console.WriteLine(LName);
}

Notice that you declare a new variable of the array type and then inside the foreach loop structure, you use the newly declared name.

Again, here’s our entire code listing now.

Image 24

Here’s our output:

Image 25

You can see that each of the distances and each of the lastNames output to the console.

All That for Args

We’ve really gone through all of that just to show you how to get the args values.

The args is an array of string so now you know that you can create a foreach loop and iterate through every item the user passes in on the command line.

Let’s add the following:

C#
foreach (string cmd in args){
    Console.WriteLine($"cmd: {cmd}");
}

I’m placing that foreach loop closer to the top of the Main method and I’m going to comment out the test code that we’ve used so far so that only the parts related to args will actually run.

Here’s what my code looks like now:

Image 26

VSC Syntax Coloring

You can see that the syntax coloring that VSC does for you sets all the comments to green. That way, you can easily see which part of the code will run and which part is just comments.

When you run the code, you’ll see something like the following (depending upon the command-line arguments you provide to the program).

Image 27

The nice thing is that if you don’t provide any arguments, then the foreach statement will understand there are no elements in the args array and won’t run the body of the foreach statement.

Image 28

Summary

This was quite a long chapter and you’ve learned a lot about the foundations that the console is built upon and how the Main function works. You’ve also learned a lot about the basics of data types and we’ve even touched upon strings and how to get input from the terminal and how to do some nice formatted output via string interpolation.

History

  • 16th August, 2020: First publication

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)