Dotnet technical interview

General Topics

OOP

Object-oriented programming (OOP) is a programming paradigm that uses objects to model real-world things. The idea is to place together with all as self-sustainable “Objects”. There are two main types of objects:

  • Classes: A class is a blueprint for creating objects. A class can be considered as a type of object.
    • A class have, name, Operations like methods and Attributes.
  • Objects: An object is an instance of a class. Also, that object is a combination of variables, functions, and data that performs a set of related activities. Those activities are called object’s behavior.

In OOP programming, there are three important concepts:

  • Encapsulation: The object is a container for data and methods.
    class User
    {
      private string name;
      public string Name
      {
          get { return name; }
          set { name = value; }
      }
    }
    
  • Inheritance: Create a new object by inheriting from an existing object. You can personalize the object by overriding the properties and methods.
    class User : UserBase
    {
      public User(string name)
      {
          Name = name;
      }
    }
    
  • Polymorphism: One object can be used in different ways. To implement polymorphism, you can use the static way method or operator overloading, or the Dynamic polymorphism overriding the method.

Let’s take a look at the example of overriding the method:

public class BaseClass
{
    public virtual string GetMethodOwnerName()
    {
       return "Base Class";
    }
}
public class ChildClass : BaseClass
{
   public override string GetMethodOwnerName()
   {
       return "Child Class";
   }
}

NOTE: You can not override the method if methods are non-virtual or static or have different signature.

There is another concept related called Shadowing or method hiding that is used to override the method without using the keyword override.

public class BaseClass
{
    public string GetMethodOwnerName()
    {
       return "Base Class";
    }
}
public class ChildClass : BaseClass
{
    public new string GetMethodOwnerName()
    {
       return "ChildClass";
    }
}

Abstract classes

Abstraction hides the implementation details and displays only the essential features of the object.

abstract class Shape
{
    public abstract double Area();
    public abstract double Perimeter();
    public void Draw()
    {
        Console.WriteLine("Drawing a shape");
    }
}
public class Circle : Shape
{
    private double radius;
    public Circle(double radius)
    {
        this.radius = radius;
    }
    public override double Area()
    {
        return Math.PI * radius * radius;
    }
    public override double Perimeter()
    {
        return 2 * Math.PI * radius;
    }
}

var circle = new Circle(5);
circle.Area();
circle.Perimeter();
circle.Draw();

Interfaces

  • The interface declares a contract or behavior that the class must implement. You can only declare properties, methods, and events without the access modifiers.
public interface IShape
{
    double Area();
    double Perimeter();
    void Draw();
}
public class Circle : Shape
{
    public double Area()
    {
        throw new NotImplementedException();
    }

    public double Perimeter()
    {
        throw new NotImplementedException();
    }

    public void Draw()
    {
        throw new NotImplementedException();
    }
}

Access Modifiers

  • private: Only accessible within the class. You can not access it from outside the class.
  • public: Accessible from anywhere.
  • protected: Only accessible from the class or derived classes.
  • internal: the object is accessible only inside its own assembly(project) but not in other assemblies.
  • protected internal: the object is accessible from the same assembly(project) and derived classes in other assemblies.
  • protected private: the object is accessible inside the containing class or in a class that derives from a containing class, but only in the same assembly(project).

Differences between interfaces and abstract classes

  • The interface declares a contract or behavior that the class must implement.
  • The abstract class can have an implementation of the methods and properties.

Neither interfaces nor abstract classes can be instantiated.

Data Types

There are two types of data types in C#: Value types and Reference value types.

The value types

The value types are the basic types of the C# language. They are the following:

int, float, bool, struct, enum, char, and many others.

When a value-type instance is created, a single space in memory is allocated to store the value. The value types lives on the stack

One important thing to remember is that the value types won’t be copied when they are passed to a method. For Example:

static void SetValue(int x){
    // Override the value of x
    x = 10;
    Console.WriteLine(x);
}

int i = 5;
Console.WriteLine(i);   // 5
SetValue(i);            // 10 
Console.WriteLine(i);   // 5

So, after you define the variable i, and then pass it to the method SetValue, the value of i is not changed (Event if that variable has been overridden).

Struct

A struct type is a lightweight object such as Point, Rectangle, and Color. Created to hold small data values that do not require inheritance.

  • Can not support inheritance
  • Can have constructors, constants, fields, methods, properties, indexers, operators, events, and nested types.
  • Can implement interfaces, same as class.
  • Are value types
  • Can not have a null reference
  • Do not have a memory overhead per new instance.
struct Point
{
    public int X;
    public int Y;
}

Point point = new Point();
Console.WriteLine(point.X); // output: 0

The Reference types

Strings, Class types, interface types, delegate types, and array types

When a reference-type instance is created, it follows the data and the objects. The value types lives on the heap. So, when you create an object and assign it into a variable var, and then, you pass that variable to another method, the variable will be a reference to the previously defined object.

static void SetFullName(Student student){
    // Override the value of x
    student.FullName = $"{student.FirstName} {student.LastName}";
    Console.WriteLine(student.FullName);
}

Student i = new Student();
i.FirstName = "John";
i.LastName = "Doe";
i.FullName = "";
Console.WriteLine(i.FullName);  // ""
SetFullName(i.FullName);        // "John Doe"
Console.WriteLine(i.FullName);  // "John Doe"

There is a special case with the String reference type: It is immutable. It means once we assign a value, it can not be changed. If we change a string value, the compiler creates a new string object in the memory and point a variable to the new memory location.

static void SetValue(string x){
    // Override the value of x
    x = "mac miller";
    Console.WriteLine(x);
}

string i = "Wiz Khalifa";
Console.WriteLine(i);   // "Wiz Khalifa"
SetValue(i);            // "mac miller"
Console.WriteLine(i);   // "Wiz Khalifa"

So, It behaves like a value type, but it is a reference type.

So, What’s the difference between Heap and Stack?

Pretty much, the stack store the value types and the heap store the reference types.
They are the responsability of the memory manager to allocate and deallocate the memory. What is being executed and what is being executed in each Thread.

The Heap in especific follow the data, in specific the objects. The Stack follow the execution.

The virtual keyword

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/virtual

The virtual keyword is used to allow it to be overrided in a derived class.

That keyword can be placed in a method, property, indexer or event declaration. And you can not mix the virtual keyword with the static, abstract, private or override keywords.

public virtual double Area()
{
    return 0;
}

By default, methods are non-virtual. You cannot override a non-virtual method.

The sealed keyword

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/sealed

The sealed keyword is used to prevent a class from being inherited.

The potential is that deriving classes could modify your classes in such a way that they would no longer work correctly or as expected.

The generic classes and methods

https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/types/generics

The generics concept is a way to create a class or method that can work with more than one type until the method is declared and instantiated by the client code. The way to do this is to use the <T> symbol in the class or method declaration. And in the client code, you can replace the T symbol with the specify the type of object that the method will work with.

public class GenericList<T>
{
    public void Add(T input) { }
}

public class TestGenericList
{
    public void Test()
    {
        // Declare a list of type int.
        GenericList<int> list1 = new GenericList<int>();
        list1.Add(1);

        // Declare a list of type string.
        GenericList<string> list2 = new GenericList<string>();
        list2.Add("");

        // Declare a list of type ExampleClass.
        GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
        list3.Add(new ExampleClass());
    }
}

With Generics, you can implement Unit of Work Patterns in your application.
https://docs.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

Unit of Work pattern

The Unit of Work pattern is a way to decouple the application from the data source. The pattern is used to encapsulate the application’s business logic and the data access logic.

So, if you change something from the data source, your domain model will not be affected. This pattern is created to avoid inject of the DbContext into the controller, instead, you can inject the IUnitOfWork respository interface into the controller.

Just a quick example:

// The Student contract repository
// It contains all the actions necesary with the database
public interface IStudentRepository : IDisposable
{
    IEnumerable<Student> GetStudents();
    Student GetStudentByID(int studentId);
    void InsertStudent(Student student);
    void DeleteStudent(int studentID);
    void UpdateStudent(Student student);
    void Save();
}

// The Student repository
// Here we implement the IStudentRepository interface and inject the DbContext in the constructor
public class StudentRepository : IStudentRepository, IDisposable
{
    private SchoolContext context;

    public StudentRepository(SchoolContext context)
    {
        this.context = context;
    }

    public IEnumerable<Student> GetStudents()
    {
        return context.Students.ToList();
    }

    public Student GetStudentByID(int id)
    {
        return context.Students.Find(id);
    }

    public void InsertStudent(Student student)
    {
        context.Students.Add(student);
    }

    public void DeleteStudent(int studentID)
    {
        Student student = context.Students.Find(studentID);
        context.Students.Remove(student);
    }

    public void UpdateStudent(Student student)
    {
        context.Entry(student).State = EntityState.Modified;
    }

    public void Save()
    {
        context.SaveChanges();
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

// The Student controller
// Here we inject the IStudentRepository interface into the constructor instead of the DbContext
 public class StudentController : Controller
{
    private IStudentRepository studentRepository;

    public StudentController(IStudentRepository studentRepository)
    {
        this.studentRepository = studentRepository;
    }

    // GET: /Student/Details/5
    public ViewResult Details(int id)
    {
        Student student = studentRepository.GetStudentByID(id);
        return View(student);
    }
 
    protected override void Dispose(bool disposing)
    {
        studentRepository.Dispose();
        base.Dispose(disposing);
    }
}

In the previous example, we have a repository that is injected into the controller. That contains all the abstraction of the database.

Now, let’s create a Generic Respository and a unit of work class. This is because we can have a lot of redundant code for each type of entity. Another reason is because we need to have all repositories in the same database context.
The reason for this is: If you create an action in the first repository, but the second repository is in a different context and fails, you have a problem because you cannot roll the entire transaction back.

// The Generic Repository
// This is the base repository that contains all the general actions necesary with the database
// IRepository.cs
public interface IRepository<TEntity>
{
    Task<IEnumerable<TEntity>> GetAllAsync();

    Task<IEnumerable<TEntity>> GetAllAsync(Expression<Func<TEntity, bool>> filter);

    Task<IEnumerable<TEntity>> GetAllAsync(Expression<Func<TEntity, bool>> filter, int take, int skip);

    Task AddAsync(TEntity entity);

    Task AddRangeAsync(IEnumerable<TEntity> entities);

    void Add(TEntity entity);

    Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> filter, bool asNoTracking = false, CancellationToken cancellationToken = default);

    void RemoveRange(IEnumerable<TEntity> entities);

    IUnitOfWork UnitOfWork { get; }
}

// Repository.cs
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    protected readonly SchoolContext Context;

    public Repository(SchoolContext context)
    {
        Context = context;
    }

    public IUnitOfWork UnitOfWork => Context;

    public async Task<IEnumerable<TEntity>> GetAllAsync() => await Context.Set<TEntity>().ToListAsync();

    public async Task<IEnumerable<TEntity>> GetAllAsync(Expression<Func<TEntity, bool>> filter) =>
        await Context.Set<TEntity>().Where(filter).ToListAsync();

    public async Task<IEnumerable<TEntity>>
        GetAllAsync(Expression<Func<TEntity, bool>> filter, int take, int skip) =>
        await Context.Set<TEntity>().Where(filter).Take(take).Skip(skip).ToListAsync();

    public async Task AddAsync(TEntity entity) => await Context.Set<TEntity>().AddAsync(entity);

    public async Task AddRangeAsync(IEnumerable<TEntity> entities) =>
        await Context.Set<TEntity>().AddRangeAsync(entities);

    public void Add(TEntity entity) => Context.Set<TEntity>().Add(entity);

    public void RemoveRange(IEnumerable<TEntity> entities) => Context.Set<TEntity>().RemoveRange(entities);

    public async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> filter, bool asNoTracking = false, CancellationToken cancellationToken = default) =>
        asNoTracking ? await Context.Set<TEntity>().AsNoTracking().SingleOrDefaultAsync(filter, cancellationToken)
            : await Context.Set<TEntity>().SingleOrDefaultAsync(filter, cancellationToken);
}

One important thing to note is that the GenericRepository class is generic. This means that it can be used with any type of entity. Methods like GetAllAsync, Insert, Delete and Update are generic and you need to specify the type of entity that you want to use in the implementation time. Now let’s create the unit of work class

// IUnitOfWork.cs
public interface IUnitOfWork
{
    Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}

// In the DbContext, we need to implement the IUnitOfWork interface
public abstract class SchoolContext : DbContext, IUnitOfWork
{
    public SchoolContext()
    {
        Database.SetInitializer<SchoolContext>(null);
    }

    public DbSet<Student> Students { get; set; }

    public virtual Task<int> SaveChangesAsync(CancellationToken cancellationToken)
    {
        return base.SaveChangesAsync(cancellationToken);
    }
}

If you want to create something more specific, you can create a class that inherits from the IRepository<T> and implements it.

public class StudentRepository : Repository<User>, IStudentRepository, IDisposable
{
    public StudentRepository(SchoolContext context) : base(context)
    {
    }

    // Here put your custom actions
}
    

In the end, we need to reference the IRepository in the DependencyInjection declaration

services.AddScoped<IStudentRepository, StudentRepository>();
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));

In the controller, just inject the IRepository in the constructor.

And save changes witn await _studentRepository.UnitOfWork.SaveChangesAsync(cancellationToken);

Dependency injection in ASP.NET Core

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-6.0

The dependency is an object used by other objects, typically with a new instance of that object. With the Dependency injection technique allows you to inject dependencies into our classes using an interface or base class to abstract dependency.

Dotnet core now supports Dependency Injection (DI) pattern. To use this feature, you need to register your dependency in the service container IServiceProvider. Typically defined in the Startup.cs file or Program.cs.

You Inject your services into the constructor of the class where it’s used. And Dotnet core will be the responsibility of creating an instance of that dependency and disposing of it when it’s no longer needed.

public interface IStudentRepository
{
    Task<IEnumerable<Student>> GetAllAsync();
}

public class StudentRepository : IStudentRepository
{
    private readonly ILogger<StudentRepository> _logger;

    public StudentRepository(ILogger<StudentRepository> logger)
    {
        _logger = logger;
    }

    public async Task<IEnumerable<Student>> GetAllAsync()
    {
        _logger.LogInformation("Getting all students");
        return new List<Student>
        {
            new Student { Id = 1, Name = "John" },
            new Student { Id = 2, Name = "Jane" }
        };
    }
} 

Now, Let’s register the service in the service container


// Program.cs
var builder = WebApplication.CreateBuilder(args);

// ...
builder.Services.AddScoped<IStudentRepository, StudentRepository>();

var app = builder.Build();

Now, Let’s inject the service in the constructor of the controller

public class StudentController : Controller
{
    private readonly IStudentRepository _studentRepository;

    public StudentController(IStudentRepository studentRepository)
    {
        _studentRepository = studentRepository;
    }

    public async Task<IActionResult> Index()
    {
        var students = await _studentRepository.GetAllAsync();
        return View(students);
    }
}

With this approach, you can inject any dependency that you need in the constructor of the controller. That makes it easy to change the implementation without modifying the controller.

Each class have his own depenendencies, the container resolves the dependencies in the graph and returns the fully resolved service. This is usually called dependency tree, dependency graph or object graph.

There are several services lifetimes to inject dependencies.

  • AddTransient: Transient objects are always different. The transient OperationId value is different in the IndexModel and in the middleware
  • AddScoped: Scoped objects are the same for a given request but differ across each new request.
  • AddSingleton: Singleton objects are the same for every request.

Garbage collection

https://docs.microsoft.com/en-us/aspnet/core/performance/memory?view=aspnetcore-6.0

The garbage collector is the process that cleans up unused objects. Each time you create a new object, the CLR assign a memory space to available in the HEAP memory. If you have a good amount of memory, the CLR will keep creating objects, but if the memory is full, the Garbage collector starts to free the memory.

That Garbage collector will stop all subprocess in executuin and will find all objects that are not used anymore from the main program and will delete them.

TODO: IDisposable

Delegates and events

A delegate is a type that represents references to methods with a particular parameter list and return type.
When you instanciate a delegate, you can associate its instance with any method with a compatible signature and return type. You can invoke or call the method through the delegate instance.

public delegate void FooDelegate();

class FooClass
{
    // custom event
    public event FooDelegate FooEvent;
}

FooClass FooObj = new FooClass()
FooObj.FooEvent += new FooDelegate();

https://docs.microsoft.com/en-us/dotnet/csharp/delegates-overview
https://docs.microsoft.com/en-us/dotnet/csharp/distinguish-delegates-events

Expression-bodied members

Expression body definitions let you provide a member’s implementation in a very concise, readable form. You can use an expression body definition whenever the logic for any supported member, such as a method or property, consists of a single expression. An expression body definition has the following general syntax:

member => expression;

public override string ToString() => $"{fname} {lname}".Trim()

Explain the .NET framework and the principles of its work

ASP.NET Web API is a framework that simplifies building HTTP services for broader range of clients (including browsers as well as mobile devices) on top of .NET Framework.

  • It works the HTTP way using standard HTTP verbs like GET, POST, PUT, DELETE, etc. for all CRUD operations
  • Complete support for routing
  • Response generated in JSON or XML format using MediaTypeFormatter
  • It has the ability to be hosted in IIS as well as self-host outside of IIS
  • Supports Model binding and Validation
  • Support for OData

Distinction between constants, read-only variables and static keywords

The const keyword is used to define constant variables, it is immutable, and that variable will not change during the program’s life. Also known as compile time constants. You must to define the value when you define the variable. It can only be used to primitive data types (Such as int, floats, chars and booleans) and strings but not with objects.


const string connectionString = "Data Source=.;Initial Catalog=MyDatabase;Integrated Security=True";

const Car car = new Car();
// Compilation Error

The read-only keyword is used to define variables or objects that can only be read but cannot be written in a place other than the constructor.


public class Car
{
    public readonly string Name = "Ford";

    public Car()
    {
        Name = "Ferrari";
        // It can be reasign only in the constructor
    }

    public void ChangeName()
    {
        Name = "BMW";
        // Compilation Error
    }
}

The keyword static is used in a variable, method or object. If something is static, this member of the class belongs to the type itself and not to an instance of this type. Pretty much, you don’t need to instance the class to use the variable or method.


public class Utility 
{
    public static int GetRandomNumber()
    {
        return new Random().Next(1, 100);
    }
}

To use it, you dont need to instanciate the class, just call the method with the class name Utility.GetRandomNumber().

If you put the static keyword in a constructor, that constructor is used to initialize the static members of the class. However, this constructor cannot accept parameters.

In summary, use the const when your variable will never change. Use the read-only keyword when you want to assign the variable once in the constructor, like dependency injection. And use the static keyword when you want to use the variable or method without instanciating the class.

https://www.infoworld.com/article/3546242/how-to-use-const-readonly-and-static-in-csharp.html

Enumerate aspects that distinguish ODBC from ADO

ODBC is a generic provider, ODBC drivers are available for almost any data source - even simple CSV-Files.
As a drawback you have fewer functions than in ADO.NET and (at least in theory) less performance.
ADO.NET is more strict to connect to a relational database.

ODBC is an open interface, which can be used by any application to communicate with any database. ADO is a wrapper around ODBC. If the database does not support OLE, you can use ODBC.

If the envionment is not SQL, you have to use ADO (Because ODBC only works with SQL).

If interoperable database components are required, then ADO should be used instead ODBC.

For 16-bit data, ODBC is the only choice.

ADO allows you to connect with multiple databases. OBDC does not.

Discuss the key differences and possible linkages between encrypting a password and applying hashing

https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/consumer-apis/password-hashing?view=aspnetcore-6.0

https://stackoverflow.com/questions/326699/difference-between-hashing-a-password-and-encrypting-it

Hashing is a one way function (well, a mapping). It’s irreversible, you apply the secure hash algorithm and you cannot get the original string back. The most you can do is to generate what’s called “a collision”, that is, finding a different string that provides the same hash. Cryptographically secure hash algorithms are designed to prevent the occurrence of collisions. You can attack a secure hash by the use of a rainbow table, which you can counteract by applying a salt to the hash before storing it.

Encrypting is a proper (two way) function. It’s reversible, you can decrypt the mangled string to get original string if you have the key.

The unsafe functionality it’s referring to is that if you encrypt the passwords, your application has the key stored somewhere and an attacker who gets access to your database (and/or code) can get the original passwords by getting both the key and the encrypted text, whereas with a hash it’s impossible.

People usually say that if a cracker owns your database or your code he doesn’t need a password, thus the difference is moot. This is naïve, because you still have the duty to protect your users’ passwords, mainly because most of them do use the same password over and over again, exposing them to a greater risk by leaking their passwords.

Reflection

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/reflection

https://www.infoworld.com/article/3027240/how-to-work-with-reflection-in-c.html

Reflection in C# is used to retrieve metadata on types at runtime. In other words, you can use reflection to inspect metadata of the types in your program dynamically – you can retrieve information on the loaded assemblies and the types defined in them. Reflection in C# is similar to RTTI (Runtime Type Information) of C++.

public class Customer
{
    public Customer()
    {
        //Default constructor
    }

    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { get; set; }

}

static void Main(string[] args)
{

    Type type = typeof(Customer);

    PropertyInfo[] propertyInfo = type.GetProperties();

    Console.WriteLine("The list of properties of the Customer class are:--");

    foreach (PropertyInfo pInfo in propertyInfo)
    {
        Console.WriteLine(pInfo.Name);
    }

    ConstructorInfo[] constructorInfo = type.GetConstructors();
    Console.WriteLine("The Customer class contains the following Constructors:--");

    foreach (ConstructorInfo c in constructorInfo)
    {
        Console.WriteLine(c);
    }

    MethodInfo[] methodInfo = type.GetMethods();

    Console.WriteLine("The methods of the Customer class are:--");
    foreach (MethodInfo temp in methodInfo)
    {
        Console.WriteLine(temp.Name);               
    }

    foreach (MethodInfo temp in methodInfo)
    {

        foreach (Attribute attribute in temp.GetCustomAttributes(true))
        {
            //Write your usual code here
        }
    }
}

SOLID Principles

https://www.educative.io/blog/solid-principles-oop-c-sharp

Design pattern

  • Creational Patterns

    • Abstract Factory: Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
    • Builder: Separate the construction of a complex object from its representation so that the same construction process can create different representations.
    • Factory Method: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
    • Prototype: Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype.
    • Singleton: Allows us to create one, and only one instance of a class during lifetime of application
  • Structural Patterns

    • Adapter: Match interfaces of different classes
    • Bridge: Separates an object’s interface from its implementation
    • Composite: A tree structure of simple and composite objects
    • Decorator: Add responsibilities to objects dynamically
    • Facade: A single class that represents an entire subsystem
    • Flyweight: A fine-grained instance used for efficient sharing
    • Proxy: An object representing another object
  • Behavioral Patterns

    • Chain of Resp: A way of passing a request between a chain of objects
    • Command: Encapsulate a command request as an object
    • Interpreter: A way to include language elements in a program
    • Iterator: Sequentially access the elements of a collection
    • Mediator: Defines simplified communication between classes
    • Memento: Capture and restore an object’s internal state
    • Observer: A way of notifying change to a number of classes
    • State: Alter an object’s behavior when its state changes
    • Strategy: Encapsulates an algorithm inside a class
    • Template Method: Defer the exact steps of an algorithm to a subclass
    • Visitor: Defines a new operation to a class without change

The “using” statement

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement

Provides a convenient syntax that ensures the correct use of IDisposable objects. Beginning in C# 8.0, the using statement ensures the correct use of IAsyncDisposable objects.

string manyLines = @"This is line one
This is line two
Here is line three
The penultimate line is line four
This is the final, fifth line.";

using var reader = new StringReader(manyLines);
string? item;
do
{
    item = reader.ReadLine();
    Console.WriteLine(item);
} while (item != null);

When the lifetime of an IDisposable object is limited to a single method, you should declare and instantiate it in the using statement or using declaration. The using declaration calls the Dispose method on the object in the correct way when it goes out of scope. The using statement causes the object itself to go out of scope as soon as Dispose is called. Within the using block, the object is read-only and can’t be modified or reassigned. A variable declared with a using declaration is read-only. If the object implements IAsyncDisposable instead of IDisposable, either using form calls the DisposeAsync and awaits the returned ValueTask. For more information on IAsyncDisposable, see Implement a DisposeAsync method.

Explain the nature of JIT

https://www.geeksforgeeks.org/what-is-just-in-time-jit-compiler-in-dot-net/

Just-In-Time compiler(JIT) is a part of Common Language Runtime (CLR) in .NET which is responsible for managing the execution of .NET programs regardless of any .NET programming language. A language-specific compiler converts the source code to the intermediate language. This intermediate language is then converted into the machine code by the Just-In-Time (JIT) compiler. This machine code is specific to the computer environment that the JIT compiler runs on.

Interview Questions

Useful links:

https://anywhere.epam.com/en/blog/-net-technical-interview-questions-personal-experience-of-a-senior-net-developer

https://gist.github.com/Whistler092/d624e4c9b126f771ac8dfa793769e098

https://www.youtube.com/watch?v=56BtZEhLyWU

https://training.epam.ua/#!/News/301?lang=en

https://www.mockquestions.com/company/EPAM+Systems/