Raihan Taher


What are Generics?

In C#, generics are used to create classes, methods, structs and other components that are not specific, but general. This allows us to use the generic component for different reasons. Generics give us some extra power of reusability in our code, which is good for an application as there would be less code which does similar work. Generics are not newly developed; they has been available since C# 2.0.

The angle brackets <>, next to the Price class is the syntax of generics in C#. By putting <> next to the class name, we are telling the compiler that this is a generic class. Furthermore, the T inside <> is a type parameter. A type parameter is like any other parameter in C# programming, except it passes a type instead of a value or reference. We created a generic Price class. To make it generic, we placed next to the class name. Here, the T is a type parameter, but it’s not something fixed that you have to use T with to represent the type parameter you can use anything to represent it. However, it is traditional to use T for the type parameter. If there are more type parameters, V and E are used. There is another popular convention when using two or more parameters, which is to name the parameter something such as TValue and TKey, instead of just V and E,
which is done for better readability. However, as you can see, we have prefixed T before the words Value and Key, which is done to distinguish between a type parameter and a general parameter.

When we run the preceding code, the type that we pass in the class will be the type of the object ob. Consequently, we can say that T is a placeholder, which will be replaced with some other concrete C# types (int, double, string, or any other complex type) in the runtime.

In the constructor, we passed a parameter of the T type and then assigned the value of the passed parameter, o, to the local variable, ob. We can do this assignment as the parameter passed in the constructor is also the T type.


Why do we need generics?

The object type can be used for any type in C#, and the preceding example can be achieved through the use of an object type. Yes, the preceding example can be achieved through the use of the object type, but there won’t be any typesafety. In contrast, generics ensure that the type-safety is there when the code gets executed. Type safety actually refers to keeping the type secure or unchangeable when executing any task in the program. This helps us reduce runtime errors.


Different constraints of generics?

There are different types of constraints available in C# generics:

Base class constraints: The idea of this constraint is that only the classes that extend a base class can be used as generic type. For example, if you have a class named Person and you use this Person class as a base for the Generic constraint, only the Person class or any other class that inherits the Person class can be used as the type argument for that generic class.

Interface constraints: Similar to the Base class constraint, we see the interface constraint when your generic class constraint is set as an Interface. Only those classes can be used in the generic method that implements that interface.

Reference type and value type constraints: When you want to differentiate between your generic class and reference types and value types, you need to use this constraint. When you use a Reference type constraint, the generic class will only accept the Reference type objects.
To achieve that, you have to extend your generic class with a class keyword. Furthermore, when you want to use a value type, you have to extend your generic class with a ‘value type’ keyword. So, when you make a value type constraint, this means that the generic will only work for value types such as int or double. No reference type, such as string or any other custom class, will work.


Generic methods

Like the Generic class, there can be generic methods, and a generic method does not necessarily have to be inside a generic class. A generic method can be inside a non generic class as well. To create a generic method, you have to place the type parameter next to the method name and before the parenthesis. The general form is given here:

access-modifier return-type method-name<type-parameter>(params)
{
   method-body
}

Here, we can see that the Hello class is not a Generic class. However, the Larger method is a generic method. This method takes two parameters and compares them, returning the larger value. This method has also implemented a constraint, which is IComparable. In the main method, we have called this generic method several times, once with int values and once with double values. In the output, we can see that the method was successfully able to compare and return the larger value.

In this example, we have used only one type of parameter, but it is possible to have more than one parameter in a generic method. We have also created a static method in this example code, but a generic method can be non static as well.

The Compiler is getting smarter, the previous code is an example of a type-inferencing in a generic method. Type-inferencing means calling a generic method without specifying the type parameter, and letting the compiler identify which type to use. This is because the compiler used type inferences to figure out the type of arguments that were passed in the methods and executed the method as if the parameter type was already given to the compiler. Because of that, when you use a type inference, it’s not allowed to provide different types of arguments in a generic method. If you need to pass different types of arguments, you should explicitly do that. You can also apply the constraints on a method that can be applied on the classes as well.


Covariance and contravariance in generics

However, from C# 4, these are also available for generic interfaces and delegates, the concepts of covariance and contravariance in generics is almost the same as it is in delegates.

Covariance: This means that the generic interface that has a T type parameter can return T or any class that is derived from T. To achieve this, the parameter should be used with the out keyword.

access-modifier interface-name<out T>
{
   method-body
}

Contravariance: The word “Contravariance” might sound a little complex, but the concept behind it is very simple. Normally, when creating a generic method, the argument we pass to it is the same type as T. If you try to pass another type of argument, it will give you a compile-time error. However, when using contravariance, you can pass the base class, which the type parameter implements. In addition, to use contravariance, there is a special syntax we have to follow.

access-modifier interface interface-name<in T>
{
   method-body
}

We will see that we have created an Interface named IFood, which uses contravariance, this means that if this interface is implemented in a generic class, that class will allow the base class of the provided type parameter. The T in the IFood interface method signature is used as a parameter in the method. Now, a class named HealthyFood implements the interface, and the method that is implemented in the class only prints a string. Then, we created two classes: Vegetable and Carrot. Carrot extends Vegetable. Both classes override the ToString() method, and return Carrot if the class is Carrot or Vegetable if the class is Vegetable.

In the main method, we create an object of the Carrot class and an object of the Vegetable class. Both of these are kept in the IFood variable. The interesting part here is that the mySelf2 variable is of the IFood type, but it holds an object of the HealthyFood type. This is only possible because of contravariance. If you remove the in keyword and try to run the program again, you will fail and the compiler will throw an error to say that this is not possible. It was only possible to run the code because of contravariance.

Raihan Taher


What is a delegate?

A delegate is a proxy, an alternative, or a representative of someone else. For example, we may read in the newspaper that a delegate from another country is coming to our country to meet a high official. This person is a delegate because they have come to our country to represent their own country. They could be a representative for the president, prime minister, or any other high official of that country. Let’s imagine that the delegate is representing the president of a country. Maybe the president was unable to attend this meeting in person for some reason, and that is why a delegate was sent on their behalf. This delegate will do the same work that the president was supposed to do on the trip and make decisions on behalf of the president. The delegate is not a fixed individual; could be any qualified person that the president chooses.

The concept of a delegate is similar in software development. We can have a functionality where a method doesn’t do the actual work that it was ask to do, but rather, it will call another method to execute that work. Furthermore, in programming, the method that doesn’t do the actual work, but passes it to another method, is called a delegate. Consequently, a delegate will actually hold a reference of a method. When the delegate is called, the referenced method will actually be called and executed. Now, you may ask, “Why should I call a delegate if it is going to call another method? Why don’t I just call the method directly?”. Well, we do this because if you directly call the method, you lose your flexibility by making your code coupled. You are hard coding the method name in your code so that, whenever that line of code will run, that method will be executed. However, with a delegate, you can decide which method to call at runtime instead of compile time.

At he very top, we can see the declaration of the delegate, the delegate keyword indicate to the compiler that we are declaring a delegate. Then we set the return type to int and named the delegate MathFunc, we also passed two int parameters in this delegate. After that we have two methods in addition to the main method, one is Add and the other is Sub, if you pay close attention to these methods, you will see that they have the same signature as the delegate, this is done deliberately, because a method can use a delegate when the method has the same signature as the delegate.


Method Group Conversion

In the previous example, we saw how can create an object of a delegate and pass the method name in the constructor, now we will look a another way of achieving the same thing. With method group conversion, you don’t need to initialize the delegate object, but you can directly assign the method to it.

We can see that instead of passing the method name in the constructor, we directly assign the method to it.


Using the static and instance methods as delegates

In the previous examples, we used static methods in the delegates, however you can also use instance methods in delegates.

To use those methods in delegates, we first have to create an object of that class and simply assign the methods to a delegate using the object instance.


Multicasting

With multicasting, you can assign more than one method to a delegate. When that delegate is executed, it runs all the methods that were assigned one after another. Using the + or += operator, you can add methods to a delegate. There is also a way to remove added methods from the delegate. To do this, you have to use the – or -= operator.

Here, we can see how our delegate executed the two methods one after the other. We have to keep in mind that it works like a queue, so the first method you add will be the first method to get executed.


Covariance and Contravariance

In the previous examples the return type and the parameters of the method and the delegate have to be the same. However, with the use of the concepts of covariance and contravariance, you can actually register methods to a delegate that don’t have the same return types or parameters. The delegate will then be able to execute them when called. Covariance is when you assign a method to a delegate that has a return type that is a derived type of the delegate’s return type. For example, if class B is derived from class A, and if the delegate returns class A, then a method can be registered to the delegate that returns class B.

On the other hand, contravariance is when a method is passed to a delegate and the parameters of the method don’t match the parameters of the delegate. Here, we have to keep in mind that the parameter type of the method has to be at least derived from the parameter type of the delegate.



Events

You can think of an event as a kind of method that gets executed in some circumstances and notifies handlers or delegates about that incident. You can also create different event handlers and assign those event handlers to an event so that, whenever that event gets fired, it will notify all the registered handlers that will perform their work.


Multicasting Events

You can multicast events in the same way that you can in a delegates. This means that you can register multiple event handlers (methods that have subscribed to the event) to an event and all of them will be executed one by one when the event gets fired. To multicast, you have to use the += sign to register event handlers to the event. You can also remove event handlers from the event by using the -= operator. When you apply multicast, the first event handler that was registered will get executed first, then the second, and so on. By multicasting, you can easily extend or reduce event handlers in your application without doing much work.


Event Guidelines From .NET

For better stability, .NET Framework has provided some guidelines for using events in C#. It’s not that you absolutely must follow these guidelines, but following these guidelines will certainly make your program more productive. Now let’s see what guidelines we need to follow.

  • The reference to the object that generated the event.
  • The type of EventArgs that will hold other important information needed by the event handlers.


Raihan Taher


Interfaces

A class is a blueprint, which means it contains the members and methods that the instantiated objects will have. An interface can also be categorized as a blueprint, but unlike a class, an interface doesn’t have any method implementation. Interfaces are more like a guideline for classes that implement the interface.

The main features of interfaces in C# are as follows:

  • Interfaces can’t have a method body; they can only have the method signature.
  • Interfaces can have methods, properties, events and indexers.
  • An interface can’t be instantiated, so no object of an interface can be created.
  • One class can extend multiple interfaces.

One of the major uses of an interface is dependency injection. By using interfaces, you can reduce the dependencies in a system.

Let’s look an example of an interface

In the preceding example, we can see that we have one interface, called IBankAccount, that has two members: Debit and Credit. Both of these methods have no implementations in the interface. In the interface, the method signatures are more like guidelines or requirements for the classes that will implement this interface. If any class implements this interface, then the class has to implement the method body.

This is a great use of the OOP concept of inheritance. The class will have to give an implementation of the methods that are mentioned in the interface. If the class doesn’t implement any of the methods of the interface, the compiler will throw an error that the class has not implemented all the methods of the interface. By language design, if an interface is implemented by a class, all the members of the interface must be taken care of in the class.


Abstrac Class

An abstract class is a special kind of class that comes with the C# programming language. This class has similar functionalities to an interface. For example, an abstract class can have methods without implementation and with implementation. Consequently, when a class implements an abstract class, the class has to override the abstract methods of the abstract class. One of the main characteristics of an abstract class is that it can’t be instantiated. An abstract class can only be used for inheritance. It might or might not have abstract methods and assessors. Sealed and abstract modifiers can’t be placed in the same class, as they have completely separate meanings.

Let’s take a look at an example of an abstract class

In the preceding example, we saw that the Dog class is implementing the Animal class, and as the Animal class has an abstract method called Move(), the Dog class must override it.


Partial Class

You can split a class, a struct, or an interface into smaller portions that can be placed in different code files. If you want to do this, you have to use the keyword partial. Even though the code is in separate code files, when compiled, they will be treated as one class altogether. There are many benefits of partial classes. One benefit is that different developers can work on different code files at a time. Another benefit is that if you are using autogenerated code and you want to extend some functionality of that autogenerated code, you can use a partial class in a separate file. Consequently, you are not directly touching the autogenerated code, but adding new functionality in the class.

The partial class has a few requirements, one of which is that all classes must have the keyword partial in their signatures. All the partial classes also have to have the same name, but the file names can be different. The partial classes also have to have the same accessibility, such as public, private, and so on. This will increase the readability of your code, and your code organization will be more structured.


Sealed Class

One of the principles of OOP is inheritance, but sometimes you may need to restrict inheritance in your code for the sake of your application’s architecture. C# provides a keyword called sealed. If this keyword is placed before a class’s signature, the class is considered a sealed class. If a class is sealed, that particular class can’t be inherited by other classes. If any class tries to inherit a sealed class, the compiler will throw an error. Structs can also be sealed, and in that case, no class can inherit that struct.

If we try to create a Dog class that will inherit the Animal class, as in the following code, then the compiler will throw an error “Dog: can not derive from sealed type Animal”, saying that the sealed Animal class can’t be inherited.


Tuples

A tuple is a data structure that holds a set of data. Tuples are mainly helpful when you want to group data and use it. Normally, a C# method can only return one value. By using a tuple, it is possible to return multiple values from a method. The Tuple class is available under the System.Tuple namespace. A tuple can be created using the Tuple<> constructor or by an abstract method named Create that comes with the Tuple class. You can fix any data type in a tuple and access it using Item1, Item2, and so on.


Properties

For security reasons, all the fields of a class shouldn’t be exposed to the outside world. Consequently, exposing private fields is done by properties in C#, which are members of that class. Underneath the properties are special methods that are called accessors. A property contains two accessors: get and set. The get accessor gets values from the field while the set accessor sets values to the field. There is a special keyword for a property, named value, this represents the value of a field. By using access modifiers, properties can have different access levels.

A property can be public, private, read only, open for read and write, and write only. If only the set accessor is implemented, this means that the only write permission is given. If both set and get accessors are implemented, this means that both read and write permissions are open for that property. C# provides a smart way of writing setter and getter methods. If you create a property in C#, you don’t have to manually write setter and getter methods for a particular field. Consequently, the common practice in C# is to create properties in a class, rather than creating fields and setter and getter methods for those fields.

The convention is that properties should be in camel case. When you create a property in camel case, a field with the same name is created internally, but in Pascal case. The value is a special keyword that actually represents the value of that property. Properties are working behind the scenes in the background, which makes the code much cleaner and easier to use. It’s very much recommended that you use properties instead of local fields.


Acces Modifiers for Classes

Access specifiers, or access modifiers, are some reserved keywords that determine the accessibility of a class, method, property, or other entity. The object oriented principle of encapsulation is achieved by using these
access specifiers in C#. In total, there are five access specifiers.

Public

The public access specifier means that there is no limitation to access the entity being modified. If a class or member is set as public, it can be accessed by other classes or programs in the same assembly, other assemblies, and even other programs that are installed in the operating system that the program is running in. Normally, the starting point of an application or main method is set as public, meaning that it can be accessed by others. To make a class public, you just need to put a public keyword before the keyword class.


Private

The private specifier is the most secure access specifier available in the C# programming language. By setting a class or member of a class as private, you are determining that the class or the member won’t be allowed to be accessed by other classes. The scope of a private member is within the class. For example, if you create a private field, that field can’t be accessed outside the class. That private field can only be used internally in that class.


Internal

If you set internal as an access specifier, this means that the entity is only accessible within the same assembly. All the classes in the assembly can access this class or member. When you build a project in .NET, it creates an assembly file, either dll or exe. There could be many assemblies in one solution, and internal members are only accessible by the classes on those particular assemblies.


Protected

Protected members are accessible by the class itself, as well as the child classes that inherit the class. Other than that, no other class can access a protected member. The protected access modifier is very useful when inheritance takes place.


Protected Internal

A protected internal is a combination of a protected access modifier and an internal access modifier. A member whose access modifier is protected internal can be accessed by all classes in the same assembly, as well as by any class that inherits it, regardless of the assembly. For example, say that you have a class named Animal in an assembly called Assembly1.dll. In the Animal class, there is a protected internal method called GetName. Any other class in Assembly1.dll can access the GetName method. Now, suppose there is another assembly named Assembly2.dll. In Assembly2.dll, there is a class named Dog that extends the Animal class. As GetName is a protected internal, even though the Dog class is in a separate assembly, it can still access the GetName method.

Raihan Taher


OOP is one of the most important programming methodologies nowadays. The whole concept depends on four main ideas, which are known as the pillars of OOP. These four pillars are as follows:

  • Abstraction
  • Encapsulation
  • Inheritance
  • Polymorphism

Abstraction: If something is abstract, it means that it doesn’t have an instance in reality but does exist as an idea or concept. In programming, we use this technique to organize our thoughts. This is one of the pillars of OOP. In C#, we have abstract classes, which implement the concept of abstraction. Abstract classes are classes that don’t have any instances, classes that implement the abstract class will implement the properties and methods of that abstract class.


Encapsulation: Encapsulation means hiding or covering. In C#, encapsulation is achieved by access modifiers. The access modifiers that are available in C# are the following:

  • Public
  • Private
  • Protected
  • Internal
  • Internal protected

Encapsulation is when you want to control other classes’ access to a certain class. For security reasons, it isn’t a good idea to make that class accessible to all classes. You can also limit access to the properties and variables of a class. Like variables and properties, you can also use access specifiers for methods. Encapsulation is a very important part of OOP as it gives us control over code


Inheritance: The word inheritance means receiving or deriving something from something else. In real life, we might talk about a child inheriting a house from his or her parents. In that case, the child has the same power over the house that his parents had. This concept of inheritance is one of the pillars of OOP. In programming, when one class is derived from another class, this is called inheritance. This means that the derived class will have the same properties as the parent class. In programming terminology, the class from which another class is derived is called the parent class, while the classes that inherit from these are called child classes.


Polymorphism: The word polymorph means many forms. To understand the concept of polymorphism properly, let’s work with an example. Let’s think about a person, such as Bill Gates. We all know that Bill Gates is a great software developer, business man, philanthropist, and also a great human being. He is one individual, but he has different roles and performs different tasks. This is polymorphism. When Bill Gates was developing software, he was playing the role of a software developer. He was thinking about the code he was writing. Later, when he became the CEO of Microsoft, he started managing people and thinking about growing the business. He’s the same person, but with different roles and different responsibilities.

In C#, there are two kind of polymorphism: static polymorphism and dynamic polymorphism. Static polymorphism is a kind of polymorphism where the role of a method is determined at compilation time, whereas, in dynamic polymorphism, the role of a method is determined at runtime. Examples of static polymorphism include method overloading and operator overloading. Writing a method with the same name as another method, but with different parameters, is called method overloading. This is a kind of polymorphism. Like method overloading, operator overloading is also a static polymorphism.

Dynamic polymorphism refers to the use of the abstract class. When you write an abstract class, no instance can be created from that abstract class. When any other class uses or implements that abstract class, the class also has to implement the abstract methods of that abstract class. As different classes can implement the abstract class and can have different implementations of abstract methods, polymorphic behavior is achieved. In this case, we have methods with the same name but different implementations.


In the next code, show an example of abstraction, encapsulation, inheritance and polymorphism.

Raihan Taher


C# is a fully Object oriented programming language “OOP”, the first word is object, an object is something that can be seen, felt, or touched; something that has physical existence in the real world, if an item is virtual, this means that it doesn’t have any physical existence and is not considered an object. The second word is oriented, which indicates a direction or something to aim for, for example, when we say that we are oriented toward the building, we mean that we are facing towards it.
The third word is programming. Programming is just giving instructions to the computer. As the computer doesn’t speak our language, we humans have to give instructions to the computer in a language that the computer understands. We humans call these instructions computer programs, as we are guiding or instructing a computer to do a particular thing.

OOP means that we write our computer programs by keeping objects at the center of our thinking. OOP is neither a tool nor a programming language it is just a concept. Some programming languages are designed to follow this concept. C# is one of the most popular OOP languages. There are other object oriented languages, such as Java, C++, and so on.
In OOP, we try to think about our software components as small objects, and create relationships between them to solve a problem.

In OOP, you derive objects from classes. Classes are one of the most important concepts in OOP, you can say they are the building blocks of OOP. A class can be described as the blueprint of an object. A class is like a template or blueprint that tells us what properties and behaviors an instance of this class will have. In most circumstances, a class itself can’t actually do anything, it is just used to create objects.

An object is an instance of a class. In other words, an object is an implementation of a class. For example, in a banking application, we have a Customer class, but that doesn’t mean that we actually have a customer in our application. To create a customer, we have to create an object of the Customer class.

A variable is something that varies, which means it is not constant. In programming, when we create a variable, the computer actually allocates a space in memory for it so that a value of the variable can be stored there.

A method is a piece of code that is written in the code file and can be reused. A method can hold many lines of code, which will be executed when it is called. Let’s take a look at the general form of a method:

access_modifier return_type name(parameter_list)
{
   //Method body
}

The first thing in the method declaration is an access_modifier, this will set the access permission of the method. Then, we have the return_type of the method, which will hold the type that the method will return, such as string, int, double, or another type. After that, we have the method name and then brackets, which indicate that it is a method. In the brackets, we have the parameter list, this can either be empty or can contain one or more parameters. Finally, we have curly brackets, which hold the method body, the code that the method will execute goes inside here. Any code following this structure will be considered a method by the C# compiler.

In every class, there is a special type of method, called a constructor. You can create a constructor in a class and program it, if you don’t create one yourself, the compiler will create a very simple constructor and use that instead. A constructor is a method that gets triggered when an object of a class is created. A constructor is mainly used to set the prerequisites of the class. A constructor doesn’t have a return type. This is because a constructor can’t return anything; it’s for initialization, not for any other type of action. Normally, the type of access is public for constructors, because otherwise no object can be instantiated. If you specifically want to prevent objects of a class from being instantiated, you can set the constructor as private. Another interesting thing is that you can have multiple constructors in a class. You might have one constructor that takes one argument and another that doesn’t take any arguments. Depending on the way in which you are initializing the object, the respective constructor will be called.

access_modifier class_name(parameter_list)
{
   //Constructor body
}

In the next example, show the use of classes, objects, variables and constructors.

Raihan Taher


Overview of C# as a Language

With the introduction of modern day programming practices, it is evident that developers are looking for more advanced constructs to help them to deliver the best software in the most effective way. Languages that evolve on top of frameworks are built to enhance the capabilities of the developers in a way that allows them to quickly build their code with less complexity so that the code is maintainable, yet readable.

There are many high level object oriented programming languages available on the market, C# is not new in the programming world and has existed for over a decade, but with the dynamic progress of the language itself creating so many newe constructs, it has already oriented, type safe, general purpose language that is built on top of the .NET framework that was developed by Microsoft and approved by the European Computer Manufacturers Association (ECMA) and the International Standarts Organization (ISO). It is built to run on the Common Language Infrastructure and can interact with any other languages that are built based on the same architecture. Inspired by C++, the language is rich in delivering the best of breed applications without handling too many complexities in code.

Evolution Of C#

C# has been one of the most dynamic languages in recent times, this language is open source, some of the major enhancements that have been put forward for the language include Generics, LINQ, Dynamics, and the async/await pattern. In the next image, we can see how the language has evolved.

Various stages of C#

Managed Code: The phrase came into beign after Microsoft declared the .NET framework, any code running in a managed environment is handled by Common Language Runtime (CLR).

Generics: Is a concept that was introduced with C# 2.0 and allow template type definition and type parameters, Generics allow the programmer to define types with opened-ended type parameters that dramatically changed the way that the programmers write code. The type-safety with dynamic typed generic templates improves readability, reusability, and code performance.

LINQ: Language Integrated Query, is a new construct of queries that can be run over structures. LINQ is very new to the programming world and gives us a glimpse of functional programming on top of object oriented general programming structure. LINQ also introduced a bunch of new interfaces in the form of the IQueryable interface, which introduced a number of libraries that can interact with the external world using LINQ. LINQ was boosted with the introduction of Lambda expressions and expression trees.

Dynamics: The dynamic programming capability helps the developer to defer the programming calls to runtime. There is a specific syntactic sugar that was introduced in the language that compiles the dynamic code on the same runtime. The version also puts forward a number of new interfaces and classes that enhace its language capabilities.

Async/await: With any language, threading or asynchronous programming is a pain. When dealing with asynchrony, the programmers have to come across many complexities that reduce the readability and maintainability of the code, with the async/await feature, programming in a asynchronous way is as simple as synchronous programming. The programming has been simplified, with all of the complexities handled by the compiler and the framework internally.

Compiler as a service: Microsoft has been working on how some parts of the source code of the compiler can be opened up to the world. Consequently, as a programmer, you are capable of querying the compiler on some of its internal work principles. C# 6.0 introduced a number of libraries that enable the developer to get an insight into the compiler, the binder, the syntax tree of the program, and so on.

Exception filters: Exception filters are newly introduced and give a program the capability to filter out certain exception types. The exception filters, being a CLR construct, have been hidden in the language throughout its lifetime, but were finally introduced with C# 6.0.

C# 8 and beyond: With C# being the most dynamic language in the market, it is constantly improving. With the newer features, such as nullable reference types, async streams, ranges and indices, interface members, and many other features that came with the latest version of C#, they have enhanced the basic features and helped programmers to take advantage of these new constructs, hence making their lives easier.

Architecture of .NET

.NET framework is still well built and makes sure to make it tiered, moduler, and hierarchical. Each tier provides specific functionalities to the user some in terms of security and some in terms of language capabilities. The tiers produce a layer of abstraction to the end users and hide most of the complexities of the native operating system as much as possible. The .NET framework is partitioned into modules, with each of them having their own distinct responsibilities. The higher tiers request specific capabilities from the lower tiers and hence it is hierarchical. In the next image shows a diagram of the .NET architecture.

 .NET architecture
.NET architecture

On its lowest level, it is the operating system that interacts with the kernel APIs that are present in the operating system. The Common Language Infrastructure connects with the Common Language Runtime, which provides services that monitor each code execution and managed memory, handles exceptions, and ensures that the application behaves as intended. Another important goal of the infrastructure is language inter-operability. The common language runtime is yet again abstracted with the .NET class libraries. This layer holds the binaries that the language is built on, and all of the compilers built on top of the libraries provide the same compiled code so that the CLR can understand the code and interact easily with one another.

Common Language Runtime: The CLR provides an interfacing between the underlying unmanaged infrastructure with the managed environment. This provides all of the basic functionalities of the managed environment in the form of garbage collection, security, and interoperability. The CLR is formed with the just-in-time compiler, which compiles the assembly code that’s produced with the specific compilers to the native calls. CLR is the most important portion of the .NET architecture.

Common Type System: Is a layer of abstraction between the language and the framework, it is evident that each of the language literals are mapped to specific CLR types. It is always preferred to use language types since the compiler takes care of the mapping of types. The CTS system is built as a hierarchy of types with System.Object at its apex. The Common Type System (CTS) is divided into two kinds, one of which is value types, which are primitives that are derived from System.ValueTypes, while anything other than that is a reference type. The value types are treated differently to the reference types. This is because while allocation of memory value types are created on a thread stack during execution, reference types are always created on the heap.

.NET framework class libraries: The framework class library lies in-between the language and the CLR, and therefore any type that’s present in the framework is exposed to the language you code. The .NET framework is formed with a good number of classes and structures, exposing never-ending functionalities that you, as a programmer, can benefit from. The class libraries are stored in the form of binaries that can be referenced directly from your program code.

Just in time compiler: .NET languages are compiled twice. During the first form of compilation, the high level language is converted into a Microsoft Intermediate Language (MSIL), which can be understood by the CLR, while the MSIL is again compiled during runtime when the program is executed. The JIT works inside the program runtime and periodically compiles the code that is expected to be required during execution.