Introduction to .NET
Why C#? First of all, we need to understand how programming languages have evolved and why certain languages are not sustainable for certain applications.
The main reason is related to source code geriatrics. For an application to be profitable in the long term, it must be maintainable and extensible. This has proven to be quite challenging with low-level languages like assembly languages, which could differ from one architecture to another. High-level languages were built on top of these, such as C, to abstract and facilitate programmers' work in writing code that could run on different hardware systems. However, even code for these languages turned out to be difficult to maintain. In short, a lot of code was still being created that was essentially the same for different use cases, informally referred to as "copy-paste" code or boilerplate code.
Later on, various programming styles were identified to help programmers maintain code better by reducing the source code. These programming styles are called paradigms, and modern languages like C#, C++, Java, Go, and Kotlin support multiple paradigms simultaneously.
For example, in the C language, it supports imperative and procedural paradigms. Imperative means that the source code specifies step by step what the program should do, while procedural means that procedure calls (functions) are used to perform a subset of steps. All modern languages support these paradigms. What the C language doesn't support, unlike C#, is Object-Oriented Programming or Functional Programming.
C# is a compiled language similar to C, but it's not compiled into machine code for a specific hardware architecture. Instead, it's compiled into an intermediate representation called Common Intermediate Language (CIL), which can be interpreted by a runtime environment (or simply runtime) like .NET. At runtime, the intermediate code is transformed into native machine code for the architecture on which the program is running. This means that once written and compiled, the code can be run on any hardware system if the runtime is implemented for it.
This feature allows C# applications to be portable across multiple platforms. This approach is similar to Java, where code is compiled into Java byte-code and then run by the Java Virtual Machine (JVM), which is equivalent to .NET. Although the .NET runtime was originally created for the Microsoft Windows operating system, it's available for other operating systems like Linux and Android.
The advantages of having a runtime are that once the code is compiled, it can run on multiple systems (compile once run anywhere), and this is a significant advantage for implementing cross-platform applications. Additionally, memory can be managed automatically by the runtime without programmer intervention. In practice, the runtime handles allocation and deallocation, and many data structures are implicitly pointers without being explicitly seen as such. The disadvantage is that it assumes more processing time and memory consumption for interpreting the intermediate code.
However, languages with a runtime still have an advantage that makes them more useful despite their lower efficiency, namely, they support reflection through the runtime. Reflection means that dynamic information about data structures in the program can be requested during runtime. Even more, data structures can be dynamically created without explicit declaration by the programmer. This is most evident in cloud systems programming, and the simplest thing that can be done with reflection is to map fields from a text or binary format, such as JSON, to a data structure automatically, field by field.