.NET: What Is It?
First, let us say that C# is a programming language, while .NET is the runtime that C# and other languages are built on. They are very different things, and when we program in C#, we need to be aware that we will intrinsically be using .NET. The version of .NET will be whatever version we download when we wish to start programming in C#. What .NET gives us as developers are libraries of code that save us from having to write our own code to perform particular tasks. For now, just think of a library as something where there are methods, small blocks of code, that perform a particular process.
Either way, for now, we simply want to understand that if we add the text to be displayed between the open and close brackets () of the WriteLine() method, it will be displayed to the console. We do not have to write the code that makes it display in the console; we just accept that the C# language and .NET handle all this for us. So let us just think of .NET as giving us code in the form of methods that will save us lots of time when developing an application. Also, we should think method when we see the open and close brackets (). Other examples of methods that we will become very familiar with are Write() and ReadLine(), but there are many built-in methods from the libraries that we will use.
We should also be aware that we can program in languages other than C#, like F# and Visual Basic (VB), but .NET is underpinning all of the programming languages supported by it. The libraries in .NET are shared between all the languages, thereby avoiding duplication of libraries and their contained methods. This makes sense as “on the surface” the WriteLine() method for displaying to the console should be the same no matter what .NET-supported language we program in. We do not need to concern ourselves that the different languages “under the hood” use different compilers.
As well as supplying developers with invaluable functionality through its methods and constructs, .NET includes a runtime environment for applications. This runtime environment is called the Common Language Runtime (CLR) , and it is the “engine” that produces the magic to allow us as developers to write code in any of the .NET-supported languages, and it will run on any device that has .NET installed. It is a “virtual machine” that lets .NET exist on a device and then it can manage .NET applications running within.
Finally, think of .NET this way: it is the environment and C# lives within it, as do other languages. Yes, it is like us: the earth is the environment, and we live within it alongside others. The environment supplies us with many useful facilities like sunshine, air, and rain, and we use these in our life as we wish to do. Our environment offers us many facilities, some we use and some we don't use. Well, the same goes when we program in C# and use .NET; we will use some of the facilities, and we will not use others.
.NET Core: What Is It?
We have just read about .NET being the runtime that C# and other languages are built on, but there is a history to .NET that can confuse us, so we will look at this history in order to help us better understand how we have reached the stage where we now just say .NET, as we have done in the preceding introductory paragraphs.
.NET Framework was launched as .NET Framework 1 in 2002 and went on to have versions up to the last version of .NET Framework 4.8. It allowed only for the development of Windows applications.
.NET Core was introduced in 2016 in an attempt to include cross-platform support, for example, for Linux and MacOS. .NET Core went from version 1 to the last version, 3.
.NET was introduced as .NET 5 in 2020 with Microsoft dropping the name Core and naming it with 5 rather than 4, in an attempt to differentiate from the .NET Framework, which was version 4. .NET 6 then followed in 2021 and in 2022 .NET 7.
So we can see that .NET Core aimed, for the first time, to give developers the facilities to develop for non-Windows devices such as mobile phones and Linux and Mac operating systems, using a standard set of libraries, which we mentioned earlier as being methods that give us functionality without having to write the code. It was not created to allow for development of desktop applications .
.NET Core was aimed at making .NET more modern, faster, and more scalable, and we could say that the philosophy behind it was to allow developers to build code once and that would run on any platform. Nowadays, we will hear a lot of talk about micro-services, small units of code living independently in the ecosystem, but able to communicate with each other when required, and .NET Core offers developers the opportunity to build micro-services.
We can therefore see that Microsoft started by offering us the .NET Framework, which ran only on Windows, and then evolved to offering us .NET Core, which allowed for cross-platform development. And now we have .NET, which runs on any platform and is the future with new releases every November. .NET is therefore a replacement for the other two.
As we are interested in developing our C# programming skills, we do not need to concern ourselves with the underlying runtime.
C# Language Versioning
There are many versions of the C# programming language , and this can cause confusion when we wish to write code and are required to choose a language level. To make the situation more confusing, there are many versions of .NET, which the C# language must work with. To help us and take the “pain” out of making the C# language version choice, the C# compiler will make the decision as to which version of C# to use, based on the .NET version we have chosen to develop our application in. This is a feature aimed at ensuring that we, as developers, are using the latest C# version for the chosen .NET version. When we use the Visual Studio Integrated Development Environment, it will use the .NET version installed on our computers, and, consequently, we will be availing of the latest version of the C# language that can be used with this .NET version. By using the highest C# version, we get the latest language features, but we still have the choice to use a different version if this is required. Caution is required when manually choosing the language level as we may be trying to use language features not supported by the selected .NET version.
C# 10 is only supported on .NET 6 and newer versions.
C# 9 is only supported on .NET 5 and newer versions.
C# 8 is only supported on .NET Core version 3.x and newer versions.
C# 7.3 is the latest supported version for the .NET Framework.
Language level within the .csproj file
.NET and C# Compilation Process
Write the code in C# or another .NET programming language.
Compile the code using the compiler, which checks the code for errors such as in syntax. We can then make the required changes.
The compiler produces Common Intermediate Language (CIL) files and these files for C# would be similar if we had written our code in another .NET programming language. That is the “beauty” of .NET – write in any language and it will compile to the same thing.
The Common Language Runtime (CLR) takes control of the process, and we previously read that the Common Language Runtime is
the “engine” that produces the magic to allow us as developers to write code in any of the .NET-supported languages, and it will run on any device that has .NET installed.
Once the intermediate language has been generated, the Common Language Runtime process uses the Just-In-Time (JIT) compiler to create the code that the specific operating system running the application requires. This means our development code has been compiled twice, first to the Common Intermediate Language and second to machine-specific code.
Compile Time and Runtime
When we choose to compile our code, the compiler takes the code and does some processing. Often the compile process finds errors in the code, for example, typographical errors, and we will be prompted to make changes to fix these compilation errors. So we are at compile time when we have written some code, and we choose to compile it in one of several different ways, for example, choosing Run from a menu, pressing a specified function key like F5, or typing a command at the command line. At compile time our code is still C#, but when the compiler finishes its processing, it will produce files in the form of an executable file, an .exe, or a dynamic link library, a .dll. These files are still not capable of being understood by the computer the code is to run on as the computer processor only understands machine code.
Now the second stage of the process is where we meet the runtime. When we want the application to run on a computer, this is the runtime, and it is the Common Language Runtime that handles this phase. At runtime the .exe or .dll files are converted to machine code capable of being read by the specific computer processor that the application is being run on. The conversion to machine code at runtime is handled by a specific part of the Common Language Runtime called the Just-In-Time compiler. Even though the compilation errors have been fixed and the code compiles, there is no guarantee that the code will run. If the code does not run at runtime, there will be runtime errors , and these are much more serious than compilation errors and present themselves where the end user can see them. This will not be a “happy” experience for the end user and should never happen, but things like this do happen. It is a rather unfortunate part of software development. A runtime error could occur because a file is not found or the memory is fully allocated, and often the consequence of a runtime error is the termination of the application.
Framework and Library
While learning to program, we will hear the terms framework and library being used widely, but it is easy to get confused about what each does and why they are different.
Library
For now, just think of a library as something where there are methods, small blocks of code, that perform a particular process.
This is a simple explanation, but we can go further and say that it is a collection of routines, blocks of code, that can be reused and have been thoroughly tried and tested. FinMath is a numerical library for the .NET platform that offers developers classes and methods for mathematical, scientific, and financial applications. As developers we use the libraries by calling the methods of the library whenever we require them in our code. We control the use of the libraries, and we should make use of libraries rather than writing our own code to perform the same functionality as library functions. All this fits in with the important concept of reusing software functionality when we can.
Think of a library in the context of building our own motor vehicle. We will choose the type of engine, the body shape, the number of seats, the wheel types, the color we will spray on the bodywork, etc. We are in control of what to include and what to leave out.
Framework
So let us just think of .NET as giving us code in the form of methods that will save us lots of time when developing an application.
This is also a simple explanation, but we can be more accurate by saying that the framework controls the calling of libraries rather than us as developers calling the libraries. Think of a framework like buying a limited-edition motor vehicle where have no choice in terms of the configuration. The vehicle is designed and built so we cannot customize it and we cannot pick the type of engine, the body shape, the number of seats, the wheel types, the color of the bodywork, etc. We are not in control of what to include. The special edition is a “constant,” and all the vehicles are identical.
Managed and Unmanaged Code
Two terms we will hear when reading about C# and .NET are managed code and unmanaged code , and while starting to learn to program in C# it is not essential to fully understand the terms, it is worth having at a conceptual overview.
At runtime the .exe or .dll files are converted to machine code capable of being read by the specific computer processor that the application is being run on.
The Common Language Runtime also takes care of wider issues such as memory allocation and garbage collection. Therefore, we do not need to concern ourselves with these things; we just concentrate on coding the C#.
Unmanaged code is code that must be managed by us as developers. We take over the role of the Common Language Runtime, and we need to manage the wider issues such as memory allocation and garbage collection. This adds many additional tasks for us as developers and this is never an ideal situation to be in. Unmanaged code will be code built and compiled outside of the .NET environment and will be in machine-readable form, unlike managed code which will be in intermediate language. Unmanaged code is therefore read directly by the hardware operating system it is running on. The preference will always be that we are using managed code, and that is what we get when using C# and the .NET framework.
Chapter Summary
In this chapter we have learned so much about the .NET framework, but it has been theoretical. However, it is theory we need to know before we start coding. We may not have fully comprehended all the theory, but be assured that many of the concepts will become crystallized as we continue our reading and when we code our C# applications.
There should be many key takeaways from this chapter, but we should clearly understand that C# is a programming language, while .NET is the framework that the C# and other languages are built on. We should also understand the difference between compile time and runtime. We learned that after compiling, our code files in the form of an executable file, an .exe, or a dynamic link library, a .dll, are produced. At runtime the .exe or .dll files are converted to machine code capable of being read by the specific computer processor that the application is being run on. Very importantly, we also learned that a library is a collection of routines, blocks of code, that have been compiled, can be reused, and have been thoroughly tried and tested. And we will use them in coding our applications.
We have started reading this book with the aim of increasing our knowledge and understanding of C#. We are at the start of the learning journey and the final target can be thought of as being at the center of a set of concentric circles. As we progress through the chapters of this book, our knowledge, understanding, and ability to program in C# will gradually increase, while the amount we have to learn will decrease. The outer circles of the concentric circles will disappear as we move closer to the circle at the center, our target. Having completed this chapter, we are at the outer circle, but we are moving closer to our target.