Skip to content

Latest commit

 

History

History
 
 

003-Language-Basics

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

C# Language Basics

This file is part of a multi-series workshop on learning C# on Linux available here.

Author: Martin Woodward

In this tutorial we will quickly cover the basics of the C# language. Future tutorials will dig deeper into features such as Language Integrated Query (LINQ) and Asynchronous programming.

Introduction

C# is a C++ style language and therefore the basic syntax will be familiar to any developer who understands C, C++ Java or related languages. It is a managed language designed to run in a Common Language Runtime benefiting from garbage collection and just-it-time compilation. C# has a simple syntax that is powerful in nature and is designed to reduce the amount of boiler-plate type code that a developer needs to create for an application. But it also provides simple mechanisms for interoperability with and library that provides a C style API including the ability to easily invoke native code and have direct access to memory through use of C++ style pointers.

Variables

Variables can be declared with a scope that is local or instance (class). C# is a statically typed language so each variable has a specific type but the compiler also supports the var keyword to mean that the type is inferred by the compiler at compilation time. Therefore the following are both valid and compile to the same thing.

int x = 1;
var y = 1;

// Therefore
int z = x + y;
// Would return an int with a value of 2.

You can also declare variables with-in the scope of the instance of the class or define constants at the class level as shown below.

    public class Program
    {
        private static readonly string _message = "Hello World!";
        public static void Main(string[] args)
        {
            Console.WriteLine(_message);
        }
    }

Note that C# has a const keyword that declares a constant at compilation time, however in general it is best practice to instead make use of the static readonly keywords to indicate to the compiler that a variable is a constant at runtime and does not change once created.

Variable and method naming is always fun. The convention is to use camalCased for private and PascalCased for class names, public methods and properties. The C# coding styles used by the .NET Core team themselves are available on GitHub.

Types

As mentioned previously, C# is statically typed. In the examples previously int and string are simply aliases to the underlying implementation of them in the Base Class Library (BCL). You can find the source code for these implementations on GitHub.

You can create your own types by defining a class for them and providing your implementation. For example

    public class Food
    {
        private string foodStuff;
        private int howNice;

        public Food(string foodStuff, int howNice)
        {
            this.foodStuff = foodStuff;
            this.howNice = howNice;
        }
    }

And then you can create a new instance of your type in your application in either way as follows

    var chocolate = new Food("Chocolate", 99);
    Food brocoli = new Food("Brocoli", 1);

Inheritance

C# is an Object Orientated language with type-safety and therefore provides strong controls around inheritance. By default, methods on a base class are not overridable unless they are declared as virtual meaning they may be overidden by a derived class or abstract which means they must be implemented in a derived class. You can define a class as abstract if you do not wish it to be directly instantiated with the new keyword and must have an implementation to be created. A class can prevent other classes from inheriting it by declaring itself or a member as sealed. C# deliberately only supports single inheritance of classes so the way that a class implements multiple behaviours is by the use of interfaces.

In the example below we bring back our Food class but add a method of Eat and mark it as virtual. Then we derrive two types of Food, Chocolate and 'Brocoli. We then overide the Eat method of Brocoli to throw an exception.

    public class Food
    {
        private string foodStuff;
        private int howNice;

        public Food(string foodStuff, int howNice)
        {
            this.foodStuff = foodStuff;
            this.howNice = howNice;
        }

        public virtual void Eat()
        {
            Console.WriteLine("nom nom");
        }
    }

    public class Chocolate : Food
    {
        public Chocolate() : base("Chocolate", 99)
        { }
    }

    public class Brocoli : Food
    {
        public Brocoli() : base("Brocoli", 1)
        { }

        public override void Eat()
        {
            throw new InvalidOperationException("Sorry, but eating Brocoli is not allowed");
        }
    }

Note

A colon (:) is used to indicate a class inherits from another class or interface. If providing a list of multiple interfaces then they are comma (,) separated. A : is also used by a constructor in a derived class to call a constructor in the base class (instead of something of using the this keyword like you would in a language like Java).

Also note that an Exception was thrown without it being explicitly declared. In c#, exceptions occur at runtime but may be caught using a try,catch,finally block.

For more information see the following sections of the C# Programming Guide

Operators

You can use all the operators you would expect for example

	int x = (1 + 5 - 2) / (2 * 2);  // x = 1
	x++; // Same as saying x = x + 1 (which would now make x=2)
	x--; // Same as saying x = x - 1 (which would make x=1)
	Console.WriteLine(++x); // Would first increment x to 2 then display the result
	Console.WriteLine(--x); // Would first decrement x to 1 then display the result
    int i = 42
    Console.WriteLine(i == 42); // Would display 'True'

The full range of operators in C# is available in the reference documentation

Note: Assignment is performed using a single = where-as the double == is the test for equality.

You can implement operators in your own types, or make use of some operators with common system types. For example to append strings you can do

	String message = "Hello " + "World!";
	Console.WriteLine("Hello World!" == message); // Would display 'True'

(To see the overload for the equality operator == in System.String take a look at the source on GitHub)

The C# compiler is also able to auto-cast types for you, therefore the following is valid syntax as the compiler will first convert the integer 42 into it's string representation, "42" and then concatenate it to the previous string.

	Console.WriteLine("The answer is :" + 42);

Collections

.NET has a rich set of collections out the box. As .NET has also provides a rich implementation of generics for a very long time, all of the collections have generic versions that are commonly used. Using them is easy

    // Create a list that must contain objects of type String
    List<string> listOfStrings = new List<string> { "First", "Second", "Third" };

    // listOfStrings can accept only strings, both on read and write.
    listOfStrings.Add("Fourth");

    // Below will throw a compile-time error, since the type parameter
    // specifies this list as containing only strings.
    listOfStrings.Add(1);

You can read more about Generics in the reference documentation where you can also find a list of common generic collections.

String Formatting

The String class in C# supports the formatting of strings where a format can be passed along with number of parameters to convert into strings and insert into the result.

	String primes = String.Format("Prime numbers {0} than {1} are: {2}, {3}, {4}, {5}, {6}",
                       "less", "ten", 1, 2, 3, 5, 7 );
	Console.WriteLine(primes);

For convenience, many methods that output messages have an overload that accepts a format string and an array of objects. Console.WriteLine is one such example, therefore the code above could be simplified into

	Console.WriteLine("Prime numbers {0} than {1} are: {2}, {3}, {4}, {5}, {6}",
                       "less", "ten", 1, 2, 3, 5, 7);

For more information see the reference documentation on Composite String Formats.

Control Flow

The if operator in C# works pretty much as you would expect, remembering that C# likes curly braces and ignores whitespace.

	// Change the values of these variables to test the results.
	bool Condition1 = true;
	bool Condition2 = true;
	bool Condition3 = true;
	bool Condition4 = true;
	
	if (Condition1)
	{
	    // Condition1 is true.
	}
	else if (Condition2)
	{
	    // Condition1 is false and Condition2 is true.
	}
	else if (Condition3)
	{
	    if (Condition4)
	    {
	        // Condition1 and Condition2 are false. Condition3 and Condition4 are true.
	    }
	    else
	    {
	        // Condition1, Condition2, and Condition4 are false. Condition3 is true.
	    }
	}
	else
	{
	    // Condition1, Condition2, and Condition3 are false.
	}

The switch statement in C# is also pretty standard except that it can also use the equality operator. For example the following is a valid switch statement in C#

		Console.WriteLine("Coffee sizes: 1=small 2=medium 3=large");
        Console.Write("Please enter your selection: ");
        string str = Console.ReadLine();
        int cost = 0;

        // Notice the goto statements in cases 2 and 3. The base cost of 25
        // is added to the additional cost for the medium and large sizes.
        switch (str)
        {
            case "1":
            case "small":
                cost += 25;
                break;
            case "2":
            case "medium":
                cost += 25;
                goto case "1";
            case "3":
            case "large":
                cost += 50;
                goto case "1";
            default:
                Console.WriteLine("Invalid selection. Please select 1, 2, or 3.");
                break;
        }
        if (cost != 0)
        {
            Console.WriteLine("Please insert {0} {1}.", cost, "cents");
        }
        Console.WriteLine("Thank you for your business.");

Loops

In c# you have the usual do while and for loops

    int x = 0;
    do 
    {
        Console.WriteLine(x++);
    } while (x < 5);

    int y = 1;
    while (n < 6) 
    {
        Console.WriteLine("n = {0}", n++);
    }

    for (int i = 1; i <= 5; i++)
    {
        Console.WriteLine(i);
    }

c# also supports the foreach loop which is recommended when iterating through arrays or collections.

    int[] fib = new int[] { 0, 1, 1, 2, 3, 5, 8, 13 };
    foreach (int element in fib)
    {
        System.Console.WriteLine(element);
    }

	List<string> listOfStrings = new List<string> { "First", "Second", "Third" };
    foreach (var element in listOfStrings)
    {
        System.Console.WriteLine(element);
    }

Any class that implements the IEnumerable or IEnumerable<T> interface can be iterated on using a foreach loop. IEnumerable is frequently used when passing around collections of data types and performing operations on them as we will see later.

Note: See the .NET Core documentation for a deep-dive on Iterators and foreach

Exercises

  1. Create a HelloWorld application in .NET and allow a user to pass in a name to say hello to when running the application by typing dotnet run Alice

  2. Modify your HelloWorld application to say hello to multiple names when passed in by typing dotnet run Alice Bob.

  3. Display a count of the number of names that you have displayed and the sum total of characters in the names.

Conclusion

In this tutorial we learnt the basics of the C# syntax, how to declare variables and classes and perform operations on them. In the following tutorial we will learn more about properties in C#.

Additional Information