.NET is a robust open-source framework for building a wide range of software applications. From user-friendly websites to complex enterprise software managing millions of transactions.
Its robust libraries and cross-platform compatibility make .NET a go-to choice for many developers.

Mastering .NET is just one step. However, handling the interview process can be even more challenging for both hiring managers and developers. This guide equips all parties with the knowledge and strategies to excel in the .NET interview setting. Within, you’ll find:

  • .NET interview questions
  • .NET core interview questions
  • ASP.NET interview questions
  • .NET C# interview questions

.NET interview requires thorough preparation. Before moving on to specific interview questions, let’s explore some tips to ensure a successful experience for both parties.

Tips for Hiring Managers

01

Ask targeted questions:

Tailor your .NET interview questions to the specific role's needs. Use the questions to assess a candidate's skills in relevant .NET technologies (ASP.NET MVC, .NET Core, etc.).
02

Evaluate problem-solving skills:

Ask candidates to solve coding challenges using .NET. Observe their approach and logic. Multiple interviews are ideal, including a technical screening and pair programming with experienced developers. Administer timed coding tests mimicking real scenarios. Evaluate candidates' proficiency in writing clean, well-structured, and maintainable .NET code.
03

Assess communication skills:

Getting things done smoothly in development requires everyone to be on the same page. Include questions that gauge a candidate's ability to explain technical concepts clearly.
04

Provide a positive interview experience:

Set a welcoming tone for the interview. This way, candidates can showcase skills comfortably and ask questions freely.

Tips for Candidates

01

Research the company:

Research the company's website ("About Us", "Our Work/Projects"), news, and LinkedIn page. Note their projects, products, and challenges. Understand their mission, culture, and values. Tailor your background to their needs. Prepare relevant experience examples that showcase fit.
02

Practice your answers:

Prepare for common .NET interview questions by rehearsing your responses beforehand.
03

Highlight your problem-solving skills:

Don't just memorize answers. Demonstrate your ability to think critically to land a job at .NET development company.
04

Ask insightful questions:

Such an approach shows genuine interest in the role and the company. Prepare thoughtful questions about the team, projects, or the technology stack they use.

With these tips in mind, you’ll be well-equipped to navigate everything confidently. Now, let’s review a curated selection of .NET interview questions and answers tailored to various experience levels.

.NET Interview Questions for Junior Developers

Junior .NET developer interviews begin with assessing foundational knowledge and skills. The initial screening evaluates basic .NET concepts and programming fundamentals. Technical rounds cover problem-solving tasks to determine a candidate’s growth potential.

 

General .NET Interview Questions

1. What are the main principles of OOP (Object-Oriented Programming)?

Encapsulation involves bundling data (properties) and methods into a single unit called a class. Encapsulation helps hide the internal implementation details of an object and provides a well-defined interface for interacting with the object.

Abstraction focuses on the essential features of an object, hiding the unnecessary details. It allows you to create abstract classes and interfaces that define a contract without specifying the implementation details.

Inheritance allows a derived class to inherit properties and methods from a base class. It promotes code reuse and helps create hierarchical relationships between classes.

Polymorphism enables objects of different classes to be treated as objects of a common superclass. It allows you to write more generic and flexible code that can work with objects of different types.

2. What are inheritance, encapsulation, abstraction, and polymorphism? Provide examples (preferably from your own experience). From which class are all classes in .NET implicitly inherited? Is multiple inheritance allowed in C#?

Inheritance is the ability of a derived class to inherit properties and methods from a base class. Encapsulation is the idea of bundling data and methods into a single unit (class) and hiding the internal implementation details.

Abstraction focuses on the essential features of an object, hiding the non-essential details. Polymorphism allows objects of different classes to be treated as objects of a common superclass.

In .NET, all classes implicitly inherit from the Object class, which is the base class for all .NET classes. Multiple inheritance is not allowed in C# but can be achieved through interfaces.

3. What is recursion?

Recursion is a programming technique where a function calls itself to solve a problem. It involves breaking down a problem into smaller, similar subproblems until a base case is reached. Recursive functions are often used to solve problems that can be defined in terms of smaller instances of the same problem, such as traversing tree-like data structures, calculating factorials, or generating Fibonacci sequences.

4. What is a lambda expression?

A lambda expression is an anonymous function that can be used as a method argument or to initialize a variable. This function concisely represents a method, allowing you to write more readable and expressive code. Lambda expressions are commonly used in LINQ queries and various event handlers.

5. What is parallel programming (multithreading), and its purpose? Which classes are used?

Parallel programming, or multithreading, is the ability of a system to support multiple threads of execution simultaneously. It improves performance by utilizing multiple processors or cores, allowing the application to perform multiple tasks concurrently. In .NET, classes like Task, Thread, and the Task Parallel Library (TPL) are commonly used for parallel programming.

6. What is JSON?

JSON (JavaScript Object Notation) is a text-based data interchange format that machines can parse and generate easily. JSON is often used as an alternative to XML to transmit data between a server and web application.

7. What is your understanding of REST?

REST (Representational State Transfer) is an architectural style for building web services that use HTTP methods (GET, POST, PUT, DELETE) to perform CRUD (Create, Read, Update, Delete) operations on resources. REST APIs offer a structured way to access and manage web services. Clients can interact with these services using standard rules and protocols.

8. Explain the SPA (Single Page Application) concept.

Single Page Applications (SPAs) concept is a web application model that initially loads a single HTML page. As users interact with the app, content updates dynamically without reloading the entire page.

9. Which GoF (Gang of Four) design patterns have you used?

Some commonly used GoF (Gang of Four) design patterns include:

  • Singleton ensures a class has only one instance and provides a global point of access to it.
  • Factory defines an interface for creating objects but lets subclasses decide which class to instantiate.
  • Adapter converts the interface of a class into another interface the client expects.
  • Observer defines a one-to-many dependency between objects. So that when one object changes state, all its dependents are notified and updated automatically.
  • Strategy defines a family of algorithms, encapsulates each one, and makes them interchangeable.

10. What is the difference between the GET and POST HTTP methods?

GET requests retrieve data from the server, and the requested data is included in the URL.
POST requests send data to the server, and the data is included in the request body.

11. What problem does Docker solve? What are its pros and cons?

Docker solves the “works on my machine” problem by packaging an application with its dependencies into a standardized unit called a container. This container ensures that the application will run consistently across different environments. Some of the key benefits of Docker include:

  • Consistent Environment: Docker containers provide a consistent, isolated runtime environment, ensuring the application behaves the same way across different systems.
  • Faster Deployment: Docker's containerization and image-based deployment model make it easier and faster to deploy applications.
  • Better Resource Utilization: Docker's efficient use of system resources, such as CPU and memory, can lead to better utilization of the underlying infrastructure.

The main downsides of Docker include increased complexity, potential security concerns, and the risk of vendor lock-in.

12. What is the fundamental difference between unit tests and integration tests?

The fundamental difference between unit tests and integration tests is the scope and focus of the tests:

Unit tests focus on testing individual system components in isolation, verifying that each unit works as expected. They target the smallest testable parts of an application, such as individual methods or classes, and aim to ensure the correctness of the implementation.

Integration tests verify that different components of a system work together correctly. They test the interactions between multiple units or modules, ensuring the overall system functions as expected. Integration tests are typically performed after unit tests. They help identify issues that may arise from integrating different application parts.

The main difference is that unit tests target individual code units, while integration tests focus on the interactions and data flow between those units. Unit tests are typically easier to write, maintain, and run, while integration tests provide a more comprehensive understanding of the system’s behavior.

 

Exception Handling

13. What is an Exception?

An Exception is an event that occurs during the execution of a program that disrupts the normal flow of the program’s instructions. Exceptions handle unexpected situations in a program, such as division by zero or invalid user input.

14. What are the purposes of try, catch, and finally blocks? In what case can the finally block not be executed?

The try block encloses code that might throw an exception.

The catch block handles the exception and provides appropriate error-handling logic.

The final block executes code that should run regardless of whether an exception is thrown or not.

The finally block might not execute if the process is terminated, such as when Environment.FailFast() is called, or the process is abruptly shut down.

15. What is a call stack? What are the key keywords you know?

The call stack is a stack data structure that stores information about the active subroutines of a computer program. It keeps track of the functions or methods that have been called and the order in which they were called. Key keywords related to the call stack include try, catch, throw, and finally.

 

.NET Framework Interview Questions

16. What is ASP.NET?

ASP.NET is a web application framework developed by Microsoft for building dynamic websites, web applications, and web services. It provides a programming model and a set of tools and libraries for developing web applications using .NET.

17. What types of Action filters exist?

The main types of Action filters in ASP.NET MVC are:

Authorization Filters: These are used to control access to actions based on authorization rules.

Action Filters: Used to perform pre-processing or post-processing logic for actions.

Result Filters: Used to modify the result of an action.

Exception Filters: Used to handle exceptions that occur during the execution of an action.

18. What is a Web Service?

A Web Service is a software system designed to support interoperable machine-to-machine interaction over a network. Standard protocols like SOAP, REST are typically used.

19. What is CLR (Common Language Runtime)?

CLR (Common Language Runtime) is the execution engine of the .NET Framework. CLR provides a runtime environment for executing .NET applications, handling memory management, code execution, and other low-level details.

20. What is Garbage Collector at a basic level?

Garbage Collector is a mechanism in .NET that automatically reclaims memory occupied by objects no longer used by the application. This mechanism scans the memory (managed heap) to find unused objects and reclaim their space for future use.

21. What is a delegate?

A delegate is a type that represents a method with a particular signature. Delegates are used to pass methods as arguments to other methods or store methods in variables, enabling function pointers in C#.

22. Is there a difference between Delegate and Action?

Delegate is a general-purpose type representing any method with a matching signature.

Action is a specific type of delegate that represents a method with a void return type and no parameters.

23. What is LINQ, and what is it used for? Provide a few examples of LINQ usage.

LINQ (Language Integrated Query) is a set of extensions to the .NET Framework that provide a generic way to query and transform data from various data sources. Examples of LINQ usage include querying collections, accessing databases using LINQ to Entities, and transforming XML data.

24. What is a namespace, and why is it needed?

A namespace is a way to organize and scope classes, interfaces, and other types in .NET. Namespaces help prevent naming collisions and make the code more maintainable by grouping related types together.

 

Data Types, Collections, and Data Structures

25. What data types do you know?

The main data types in C# can be divided into value and reference types.

  • Value types: Include the primitive data types (e.g., int, float, bool, char, byte) and user-defined structs. Store their data directly in memory.
  • Reference types: Include classes, interfaces, delegates, and arrays. Store a reference (pointer) to the actual data, which is stored on the managed heap.

26. What primitive types do you know?

The primitive data types in C# include:

Signed integer types: sbyte, short, int, long
Unsigned integer types: byte, ushort, uint, ulong
Floating-point types: float, double
Decimal type: decimal
Boolean type: bool
Character type: char

27. What is a Nullable type?

A Nullable type is a value type that can also have a null value. It’s denoted with the ? suffix, e.g., int?. Nullable types are useful when you need to represent the absence of a value. Such as when working with databases where a column may have a null value.

28. What is a value type, and what is a reference type? What of these is a class, and what is a struct? In which area of memory are they stored?

Value types store their data directly in memory, while reference types store a reference (pointer) to the data. Classes are reference types, while structs are value types.
Value types are stored on the stack, while reference types are stored on the managed heap.

29. How do value and reference types differ? Is String a reference or a value type?

Value types are copied by value, creating a copy of the actual data.
Reference types are copied by reference, creating a copy of the reference (pointer).
String is a reference type in C#, not a value type.

30. What is the difference between StringBuilder and String?

StringBuilder is a mutable string type, meaning its contents can be modified after creation.
String is an immutable type, meaning its contents cannot be changed once created. StringBuilder is more efficient for concatenating strings, as it avoids the overhead of creating new string objects.

31. What are generics? What problems do they solve?

Generics allow you to write code that works with different data types without knowing the specific type at compile-time. They help with type safety, code reuse, and remove the need for explicit casting.

32. What is boxing/unboxing?

Boxing is the process of converting a value type to a reference type instance.
Unboxing is the opposite, converting a reference type back to a value type.
Boxing and unboxing can incur a performance penalty as they involve additional memory allocations and type conversions.

33. What are Array, List, HashSet, Dictionary? Provide examples of using these data structures. What is the time complexity of operations with them (search, insertion, deletion)?

Array: A fixed-size collection of elements of the same type. Time complexity: Access O(1), Insertion/Deletion O(n).
Example: int[] numbers = { 1, 2, 3, 4, 5 };

List<T>: A dynamic array that can grow and shrink in size. Time complexity: Access O(1), Insertion/Deletion at the end O(1), Insertion/Deletion in the middle O(n).
Example: List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

HashSet<T>: An unordered collection of unique elements. Time complexity: Add/Remove/Contains O(1) on average.Example: HashSet<string> names = new HashSet<string> { “Alice”, “Bob”, “Charlie” };

Dictionary<TKey, TValue>: A collection of key-value pairs. Time complexity: Add/Remove/Get O(1) on average. Example: Dictionary<int, string> idToName = new Dictionary<int, string> { { 1, “Alice” }, { 2, “Bob” }, { 3, “Charlie” } };

34. What clections do you know?

Queue<T> and Stack<T>: First-in-first-out (FIFO) and last-in-first-out (LIFO) collections, respectively.
SortedSet<T> and SortedDictionary<TKey, TValue>: Sorted versions of HashSet and Dictionary.
ObservableCollection<T>: A collection that provides notification when items are added, removed or the whole collection is refreshed.

35. What does the yield operator do?

The yield keyword returns an enumerable sequence of values from a method or iterator block, one at a time, instead of constructing the entire array or collection in memory. It allows you to work with data sequences too large to fit in memory all at once. When you iterate over a method that uses yield, the method generates the values on-the-fly, rather than creating a complete collection upfront.

 

Classes, Structures, and Interfaces

36. What is a class?

A class is a blueprint or template for creating objects. It encapsulates data (fields) and functionality (methods) into a single unit, providing a way to model real-world entities or concepts in software.

37. How does a class differ from an abstract class?

The key differences between a class and an abstract class are:

An abstract class cannot be instantiated directly, while a regular class can. It can have both abstract and non-abstract (implemented) methods, while a regular class can only have non-abstract methods.
A class can inherit from only one abstract class but implement multiple interfaces.

38. How does an abstract class differ from an interface? What are interfaces for, and what tasks do they perform?

The main differences between an abstract class and an interface are:

Abstract classes can have both abstract and non-abstract members.
Interfaces can only have abstract members (except for static and extension methods in C# 8.0 and later).
Abstract classes can have access modifiers and can provide method implementations.
Interfaces can only have public abstract members.
A class can inherit from only one abstract class but implement multiple interfaces.
Interfaces are used to achieve abstraction and define a contract.

39. What access modifiers do you know?

The access modifiers in C# are:

public: The type or member is accessible from anywhere.
private: The type or member is accessible only within the containing class.
protected: The type or member is accessible within the containing class or any derived classes.
internal: The type or member is accessible within the same assembly.
protected internal: The type or member is accessible within the same assembly or any derived classes.

40. What is the difference between regular and static classes?

A regular class can be instantiated and has both static and instance members. A static class cannot be instantiated and contains only static members. Static classes are useful for providing utility methods or constants that don’t require any instance state.

41. What is the difference in method overriding between the keywords new and override?

When overriding a method, the new keyword hides the base class implementation. In contrast, override replaces the base class implementation with the derived class implementation.

42. What is the difference between const and readonly?

const is a compile-time constant.
readonly can be set at runtime but cannot be changed after initialization.
const values are inlined by the compiler.
readonly fields are stored as instance fields.

43. The difference between a structure and a class. Provide examples of structures.

Structs are value types, while classes are reference types. Structs are lightweight, suitable for small, simple data structures, and are stored on the stack. Examples of structs include Point, Rectangle, and DateTime.

44. Can an instance of a structure be stored in the heap? How can you do this?

Structs are value types and cannot be directly stored in the heap. To store a struct in the heap, you would need to wrap it in a class or use boxing.

 

Asynchronicity

45. What is asynchronicity, and how does it differ from multithreading?

Asynchronicity allows a program to perform other tasks while waiting for a long-running operation to complete without blocking the main thread. It is different from multithreading, which involves running multiple threads concurrently. With asynchronicity, the application can continue executing other code while the asynchronous operation is in progress, improving overall responsiveness and efficiency. On the other hand, multithreading involves parallel execution of multiple threads, which can be useful for CPU-bound tasks but requires more complex synchronization and resource management.

46. What keywords are used to work with asynchronicity in code?

The keywords used for asynchronicity in C# are async and await.

47. What do the keywords async/await mean?

The async keyword marks a method as asynchronous, allowing it to use the await keyword to pause the execution of the method until the awaited asynchronous operation completes. It enables the method to return control to the caller while the asynchronous operation is in progress, improving the application’s overall responsiveness.

 

Databases

48. The difference between relational and non-relational databases, the pros and cons of using both options.

Relational databases (SQL) store data in tables with defined schemas, using a structured query language (SQL) for data manipulation and retrieval. They excel at handling structured data and complex queries. Relational databases are generally stronger regarding data integrity, consistency, and complex querying.

Non-relational (NoSQL) databases use flexible, schema-less data models, such as key-value, document-oriented, or graph-based. They are better suited for handling unstructured data and high scalability requirements. Non-relational databases offer higher availability, partition tolerance, and scalability.

49. What are indexes in RDBMS?

Indexes in RDBMS are additional data structures that improve the speed of data retrieval operations, such as SELECT queries. Indexes create a sorted list of values from a table column and pointers to the corresponding rows.

50. What types of JOINs exist in SQL?

The main types of SQL JOINs are:

INNER JOIN: Returns rows that have matching values in both tables.
LEFT JOIN (LEFT OUTER JOIN): Returns all rows from the left table and the matching rows from the right table.
RIGHT JOIN (RIGHT OUTER JOIN): Returns all rows from the right table and the matching rows from the left table.
FULL JOIN (FULL OUTER JOIN): Returns all rows from both tables, whether or not there is a match.

 

Testing

51. What is the fundamental difference between unit tests and integration tests?

The fundamental difference between unit tests and integration tests is the scope and focus of the tests:

Unit tests focus on testing individual units or components of a system in isolation, verifying that each unit works as expected. They target the smallest testable parts of an application, such as individual methods or classes, and aim to ensure the correctness of the implementation.

Integration tests, on the other hand, verify that different components of a system work together correctly. They test the interactions between multiple units or modules, ensuring the overall system functions as expected. Integration tests are typically performed after unit tests, and they help identify issues that may arise from integrating different parts of the application.

The main difference is that unit tests target individual units of code, while integration tests focus on the interactions and data flow between those units. Unit tests are typically easier to write, maintain, and run, while integration tests provide a more comprehensive understanding of the system’s behavior.

52. What are unit tests used for?

Unit tests are used to verify the correctness of individual units of code (methods, classes, or components) in isolation. By testing units in isolation, you ensure each building block functions correctly, leading to a reliable application.

53. What are the advantages and disadvantages of using unit tests?

Advantages of unit tests include catching bugs early, improving code quality, and providing a safety net for future changes. Disadvantages include additional development effort and the potential for tests to become outdated over time.

54. From what three logical blocks is a unit test composed?

A unit test consists of three logical blocks:
Arrange: Set up the necessary test data and preconditions.
Act: Execute the code being tested.
Assert: Verify that the observed outcome matches the expected outcome.

.NET Interview Questions for Middle Developers

Mid-level .NET interviews gauge candidates’ deeper technical proficiency. Initial rounds screen intermediate .NET concepts and design patterns. Technical stages involve complex coding and architecture discussions. Candidates may also face system design and database modeling questions.

 

General .NET Interview Questions

55. Describe in as much detail as possible what happens at the HTTP level when you type google.com in a browser.

When you type “google.com” in a browser, the following happens at the HTTP level:

The browser first resolves the domain name “google.com” to an IP address using the Domain Name System (DNS). The browser sends a DNS query to a DNS server, which looks up the IP address associated with the domain.

Once the IP address is obtained, the browser initiates a TCP connection with the web server at that IP address, using the HTTP protocol on port 80 (the default HTTP port).

The browser then sends an HTTP GET request to the web server, requesting the root resource (usually the home page). The request includes headers with information about the client, such as the browser type, accepted file types, and other metadata.

The web server receives the request, processes it, and generates a response. The response includes an HTTP status code (e.g., 200 for successful, 404 for not found), response headers with metadata about the response, and the web page’s actual content (usually in HTML format).

The browser receives the response, parses the HTML, and renders the web page. As the page loads, the browser may send additional requests for resources referenced in the HTML, such as images, stylesheets, and JavaScript files.

The browser continues to fetch all the necessary resources and assembles the complete web page, displaying it to the user.

HTTPS (Hypertext Transfer Protocol Secure) is a secure version of the HTTP protocol that adds encryption to the communication between the client (browser) and the web server.

56. How does HTTPS work?

HTTPS works as follows:

01

The client (browser) initiates a connection to the web server using the HTTPS protocol, typically on port 443.
02

The web server presents its SSL/TLS certificate, which contains its public key and other information about the server's identity.
03

The client verifies the server's certificate, checking that it was issued by a trusted Certificate Authority (CA) and that the domain name matches the expected website.
04

The client and server establish a secure, encrypted connection if the certificate is valid.
05

All subsequent communication between the client and server is encrypted.

HTTPS helps protect against man-in-the-middle attacks, eavesdropping, and other security threats.

57. What is your understanding of SOLID principles?

The SOLID principles are a set of guidelines for writing clean, maintainable, and extensible object-oriented code:

  • Single Responsibility Principle (SRP): A class should have only one reason to change.
  • Open/Closed Principle (OCP): Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.
  • Liskov Substitution Principle (LSP): Subtypes must be substitutable for their base types.
  • Clients should not be forced to depend on interfaces they do not use.
  • Dependency Inversion Principle (DIP): High-level modules should not depend on low-level modules. Both should depend on abstractions.

Adhering to the SOLID principles helps create modular, flexible, and maintainable code that is easier to understand, test, and extend over time.

58. What serialization protocols do you know, and where are they used?

Some common serialization protocols used in .NET include:

  • JSON (JavaScript Object Notation):A lightweight, text-based format used for data exchange. Commonly used for web services and APIs.
  • XML (Extensible Markup Language): A more verbose, structured format widely used for data exchange and configuration files.
  • Binary serialization: A compact, binary format for serializing .NET objects. Useful for transmitting data over the network or persisting objects to storage.
  • Protocol Buffers (protobuf): A binary serialization format developed by Google, optimized for performance and size.

The choice of serialization protocol depends on factors such as the data’s size and complexity and the system’s compatibility needs.

59. What is a pure function, and what are its advantages?

A pure function is a function that:

  • Always returns the same output for a given input (no side effects).
  • Does not modify any state outside of its local scope.

The advantages of using pure functions include:

  • Predictability: Pure functions always produce the same output for the same input, making the code more predictable and easier to reason about.
  • Testability: Pure functions can be easily tested in isolation, as they don't depend on external state.
  • Parallelism: Pure functions can be safely executed in parallel, as they don't interfere with each other.
  • Referential transparency: Expressions using pure functions can be replaced by their result without affecting the program's behavior.

Pure functions promote modularity, testability, and functional programming principles, leading to more maintainable and robust code.

60. What is dependency injection, and why is it useful?

Dependency Injection (DI) is a design pattern that allows you to decouple the creation and management of object dependencies from the objects that depend on them. Instead of directly creating dependencies, objects receive their dependencies from an external source. For example, through the constructor, method parameters, or properties.

DI is useful because it:

  • Promotes loose coupling between components, making the code more modular and testable.
  • Allows for easy substitution of implementations, enabling flexibility and adaptability.
  • Facilitates the use of abstractions, which can improve the system's overall design.
  • Simplifies the management of object lifetime and resource allocation.

By using DI, you can create more maintainable, extensible, and testable applications, as the dependencies are managed externally.

61. What are cohesion and coupling, and what do they mean?

Cohesion and coupling are two fundamental software design principles:

Cohesion refers to the degree to which the elements inside a module (e.g., a class or a function) belong together. High cohesion means that the elements within a module are strongly related and work together to achieve a specific purpose. Low cohesion indicates that the elements within a module are loosely related or unrelated, making the module harder to understand and maintain.

Coupling refers to the degree of interdependence between modules. Low coupling means that modules have minimal dependencies on each other, making the system more flexible and easier to modify. High coupling indicates that modules have strong dependencies, which can make the system more rigid and harder to change.

62. What are IaaS, PaaS, and SaaS, and what are the differences between them?

IaaS, PaaS, and SaaS are different service models in cloud computing:

IaaS (Infrastructure as a Service): Provides access to virtualized computing resources, such as virtual machines, storage, and networking. The cloud provider manages the underlying infrastructure, while the user controls the operating system, applications, and data.

PaaS (Platform as a Service): Provides a platform for developing, testing, and deploying applications, including the operating system, middleware, and runtime environment. The cloud provider manages the underlying infrastructure and platform while the user focuses on deploying and managing their applications.

SaaS (Software as a Service): Provides access to software applications hosted and managed by the cloud provider. Users access the software through a web browser or API; the cloud provider is responsible for the infrastructure, platform, and application management.

The main differences lie in the level of control and responsibility the user has over the various components of the system. IaaS offers the most control, PaaS offers a balance of control and abstraction, and SaaS offers the least control but the highest level of abstraction.

63. What debugging techniques do you use?

Some common debugging techniques include:

  • Setting breakpoints in the code to pause execution and inspect variables.
  • Stepping through the code line by line to understand the flow of execution.
  • Using the debugger's "Watch" and "Immediate" windows to evaluate expressions and monitor variable values.
  • Leveraging logging and tracing to output relevant information during program execution.
  • Analyzing stack traces to identify the root cause of exceptions or unexpected behavior.
  • Profiling the application to identify performance bottlenecks and optimize the code.
  • Leveraging debugging tools, such as PerfView, dotTrace, or Visual Studio's built-in profiler to gain deeper insights into the application's runtime behavior.

The choice of debugging technique depends on the nature of the problem, the complexity of the application, and the available tooling.

64. What design patterns do you know? Explain the essence of the ones you mentioned.

Some design patterns include:

  • Singleton ensures a class has only one instance and provides a global point of access to it. Useful for managing global state or resources.
  • Factory defines an interface for creating objects but lets subclasses decide which class to instantiate. Helpful for creating objects without exposing the creation logic.
  • Adapter converts the interface of a class into another interface the client expects. Allows classes with incompatible interfaces to work together.
  • Observer defines a one-to-many dependency between objects. So that when one object changes state, all its dependents are notified and updated automatically. Useful for implementing event-driven architectures.
  • Strategy defines a family of algorithms, encapsulates each one, and makes them interchangeable. Allows the algorithm used by a class to be selected at runtime.

These patterns help promote modularity, flexibility, and reusability in software design, making the code more maintainable over time.

65. What is the essence of the Singleton pattern? Why is it also called an anti-pattern?

The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. The essence of the Singleton pattern is to have a private constructor and a static method that returns the single instance of the class.

The Singleton pattern is sometimes considered an anti-pattern because it can introduce a global state, making the code harder to test.

Alternatives to the Singleton pattern include using Dependency Injection to manage the lifetime of objects or using static classes (in C#) to provide global access to functionality without needing a Singleton instance.

66. What is the purpose of the Strategy pattern?

The purpose of the Strategy pattern is to define a family of algorithms, encapsulate each one, and make them interchangeable. This way, the algorithm can be used by a class to be selected at runtime rather than being hard-coded.

The key benefits of the Strategy pattern are:

  • Flexibility: By encapsulating different algorithms in separate classes, the Strategy pattern makes it easy to add new algorithms or modify existing ones without changing the context in which they are used.
  • Reduced conditional logic: Instead of using conditional statements to determine which algorithm to use, the Strategy pattern delegates the choice of algorithm to the client, making the code more readable and maintainable.
  • Testability: The algorithms can be tested in isolation, as they are decoupled from the context in which they are used.

The Strategy pattern is useful in scenarios where you need to support multiple algorithms or behaviors.

67. What are the key differences between distributed systems and monolithic ones?

The key differences between distributed systems and monolithic ones are:

  • Architecture: Distributed systems are composed of multiple, independently deployable components that communicate with each other. Monolithic systems are a single, tightly coupled application.
  • Scalability:Distributed systems can scale individual components horizontally, adding more instances as needed. Monolithic systems can be more challenging to scale, as the entire application needs to be scaled.
  • Complexity: Distributed systems are generally more complex. They need to handle issues like network failures, eventual consistency, and coordinating the interactions between components.
  • Development and Deployment: Distributed systems allow for independent development and deployment of components. Monolithic systems require the entire application to be deployed together.
  • Reliability: Distributed systems can be more resilient, as the failure of one component does not necessarily bring down the entire system. Monolithic systems have a single point of failure.

68. What design patterns for distributed systems do you know?

Some design patterns used in distributed systems include:

  • Microservices: Organizes an application as a collection of loosely coupled, independently deployable services.
  • API Gateway: Serves as a single entry point for clients to access the underlying microservices.
  • Circuit Breaker: Prevents cascading failures in distributed systems by stopping calls to a failing service.
  • Event-Driven Architecture: Uses an event bus or message broker to decouple the components of a distributed system.
  • CQRS (Command Query Responsibility Segregation): Separates the read and write operations responsibilities, often used in event-driven architectures.

69. What are the principles of a Message Bus? Why can duplicates occur in queues?

The principles of a Message Bus are:

  • Loose Coupling: The components of the system are decoupled, communicating asynchronously through the message bus.
  • Scalability: The message bus can handle increasing message loads by adding more message brokers or consumers.
  • Reliability: To ensure reliable delivery, the message bus provides features like message persistence, retries, and dead-letter queues.

Duplicates can occur in queues for various reasons, such as network failures, process restarts, or message redelivery. To handle this, consumers should be designed to be idempotent, meaning they can safely process the same message multiple times.

70. What principles of building idempotent services do you know?

Asynchronous methods in .NET allow a calling thread to initiate an operation and continue executing other tasks without waiting for the operation to complete. This is achieved through the use of the async and await keywords.

When an async method is called, it returns a Task or Task<T> object, representing the pending operation. The calling thread can then await the completion of the task, at which point the method will resume execution with the result of the asynchronous operation.

71. Explain how asynchronous methods work. How does asynchronicity differ from parallelism?

The key difference between asynchronicity and parallelism is:

Asynchronicity allows a single thread to perform other work while waiting for a long-running operation to complete without blocking the main thread.

Parallelism involves concurrently executing multiple threads, each working on a different task. Parallelism is useful for CPU-bound tasks that can be divided into smaller, independent subtasks. However, it requires more complex synchronization and resource management compared to asynchronicity.

In summary, asynchronicity enables a single thread to be more productive by allowing it to switch between multiple tasks. In contrast, parallelism utilizes multiple threads to work on tasks concurrently. Both approaches can be used to improve the performance and scalability of applications, but they address different types of problems.

 

.NET Framework Interview Questions

72. What exceptions can’t be caught in a catch block?

In C#, certain exceptions cannot be caught in a catch block. These exceptions are known as unhandled exceptions, and they typically occur due to critical errors or resource constraints. Some examples of exceptions that cannot be caught include:

  • StackOverflowException: This exception occurs when the program runs out of available memory on the stack.
  • OutOfMemoryException: This exception occurs when the program runs out of available memory on the managed heap.
  • ThreadAbortException: This exception occurs when a thread is aborted due to a call to the Thread.Abort method.
  • ExecutionEngineException: This exception occurs when there is an internal error in the common language runtime (CLR).

These exceptions are considered as unhandled exceptions, and they usually result in the termination of the application or the AppDomain.

73. What is the difference between a .NET Standard Class Library and a .NET Core Class Library?

The main difference between a .NET Standard Class Library and a .NET Core Class Library lies in their target frameworks and compatibility:

.NET Standard Class Library: A .NET Standard Class Library is a project type that targets the .NET Standard, a formal specification of APIs intended to be available on all .NET implementations. The .NET Standard enables code sharing across different .NET platforms, including .NET Core, .NET Framework, Xamarin, and others. However, it doesn’t provide any implementation details or runtime components. Instead, it defines a set of APIs the underlying platform must implement.

.NET Core Class Library: A .NET Core Class Library is a project type specifically targeting the .NET Core runtime. It is designed to run on the .NET Core runtime, a cross-platform implementation of .NET. Unlike the .NET Standard Class Library, a .NET Core Class Library can take advantage of platform-specific features and APIs provided by .NET Core.

In general, if you need to create a library that needs to be shared across multiple .NET platforms (e.g., .NET Framework, .NET Core, Xamarin), you would choose a .NET Standard Class Library. However, if you are building a library that will only be used within a .NET Core application, you might prefer a .NET Core Class Library, as it can take advantage of .NET Core-specific features and optimizations.

74. Explain the difference between deferred and immediate execution in LINQ.

In LINQ (Language Integrated Query), there are two types of execution modes: deferred execution and immediate execution. The difference lies in when the query is actually evaluated and executed.

Deferred Execution:
In deferred execution mode, the LINQ query is not executed until the result is actually enumerated or accessed. The query is constructed and stored as an expression tree, and the actual evaluation is deferred until the last possible moment. Deferred execution is the default behavior for most LINQ queries.

Immediate Execution:
In immediate execution mode, the LINQ query is executed immediately, and the results are stored in a data structure (e.g., a list or array). This mode is typically used when you need to work with the query results multiple times or when you need to perform operations that cannot be expressed as a query expression.

75. What is the purpose of the ConfigureServices method in Startup.cs?

In ASP.NET Core applications, the ConfigureServices method in the Startup class is used to configure and register various services that the application will use. It is part of the application’s startup process and is called by the runtime during the application’s initialization.

The main purpose of the ConfigureServices method is to set up and configure the services that the application will use, such as:

  • Dependency Injection (DI) Services: This method registers services with the built-in Dependency Injection container. Depending on the desired lifetime and usage patterns, services can be registered as singletons, scoped, or transient instances.
  • Database Context Initialization: If the application uses a database, the ConfigureServices method is often used to configure and register the database context with the DI container.
  • Options Configuration: This method can be used to configure and bind application settings or options to specific classes or services.
  • Authentication and Authorization Services: Services related to authentication and authorization can be configured and registered using the ConfigureServices method.
  • MVC Services: For ASP.NET Core MVC applications, the ConfigureServices method adds and configures MVC services, such as model binders, view engines, and other MVC-related components.
  • Third-Party Services: Any third-party libraries or packages that provide services can be configured and registered using the ConfigureServices method.

By registering and configuring services using the ConfigureServices method, the application can take advantage of the built-in Dependency Injection system.

76. What is the difference between services.AddTransient and services.AddScoped in ASP.NET Core?

In ASP.NET Core, services.AddTransient and services.AddScoped are methods used to register services with the built-in Dependency Injection (DI) container. The main difference between them lies in the lifetime and scope of the registered service instances.

services.AddTransient:
When a service is registered as transient using services.AddTransient, a new service instance is created every time it is requested from the DI container. It means that a new instance of that service will be created each time a transient service is injected into another class or component.
Transient services are suitable for stateless services or services that do not maintain any state across multiple requests.

services.AddScoped:
When a service is registered as scoped using services.AddScoped, a single instance of the service is created and shared within the same scope. In ASP.NET Core, the scope is typically tied to the current HTTP request. This means that within the same HTTP request, the same instance of the scoped service will be used for all instances that require that service.However, for a new HTTP request, a new instance of the scoped service will be created and used for that request’s scope.

Scoped services are suitable for services that maintain a state related to the current request or operation, such as services that interact with the HTTP context or databases.

The main difference between transient and scoped services lies in the lifetime and sharing of instances:

Transient: A new instance is created every time the service is requested.
Scoped: A single instance is created and shared within the same scope (e.g., HTTP request).

In general, you should use transient services for stateless services or services that do not maintain any state across multiple requests. Scoped services should be used for services that need to maintain state related to the current request or operation.

77. What is Kestrel?

Kestrel is a cross-platform web server for ASP.NET Core applications. It is an open-source web server that was developed by Microsoft as part of the .NET Core project. Kestrel is designed to be lightweight, high-performance, and cross-platform, making it suitable for various deployment scenarios.

Here are some key features and characteristics of Kestrel:

  • Cross-Platform: Kestrel is a cross-platform web server that can run on multiple operating systems, including Windows, Linux, and macOS.
  • Lightweight and High-Performance: Kestrel is designed to be lightweight and highly performant. It uses asynchronous I/O operations and a lean architecture, efficiently handling many concurrent connections. Integration with ASP.NET Core: Kestrel is tightly integrated with ASP.NET Core and is the default web server hosting ASP.NET Core applications.
  • Modular Design: Kestrel has a modular design, allowing developers to configure and extend its functionality. For example, developers can add support for additional protocols or implement custom middleware components.
  • Reverse Proxy Support: While Kestrel is a capable web server, exposing it directly to the internet is not recommended. Instead, it is typically used in conjunction with a reverse proxy server. Such as Internet Information Services (IIS), Nginx, or Apache, which handles tasks like SSL termination, load balancing, and static file serving. Containerization and Cloud Deployments: Kestrel's lightweight and cross-platform nature makes it well-suited for containerized deployments and cloud environments.

Kestrel is designed to be a lean and efficient web server for ASP.NET Core applications, providing high performance and cross-platform support. However, it is typically used in conjunction with a reverse proxy server in production environments to ensure secure and scalable deployments.

78. Describe the ASP.NET MVC request pipeline.

The ASP.NET MVC request pipeline is a series of steps that occur when an ASP.NET MVC application processes an incoming HTTP request. The request pipeline is responsible for handling the request, routing it to the appropriate controller and action method, and executing the action method. Here’s a breakdown of the main steps in the ASP.NET MVC request pipeline:

  • Routing: The first step in the pipeline is to route the incoming request to the appropriate controller and action method based on the URL and routing rules defined in the application.
  • Model Binding: The model binding process occurs if the action method expects input parameters. During model binding, the framework attempts to map the incoming request data (query string, form data, etc.) to the action method's parameters.
  • Action Filters: Any action filters configured for the controller or action are executed before executing the action method. Action filters allow developers to add custom logic that runs before or after the action method execution.
  • Action Method Execution: The appropriate action method on the controller is executed, and any logic within the method is performed.
  • Action Result Creation: After the action method execution, an action result is created. The action result represents the outcome of the action method and contains the data or instructions for generating the response.
  • Result Filters: Any result filters configured for the controller or action are executed. Result filters allow developers to modify or add logic to the action result before it is rendered.
  • View Engine Processing: If the action result is a view result, the view engine processes the corresponding view template and generates the final HTML output.
  • Response: The final response, which may include the HTML output from the view engine or other data (such as JSON or XML), is sent back to the client.

79. How do you configure caching of HTTP responses in ASP.NET Web API?

In ASP.NET Web API, you can enable caching of HTTP responses by setting the appropriate response headers. The key headers for caching are:

– CacheControl: This header specifies caching policies like public/private caching, max-age (duration for which the response can be cached), and other caching directives.

– Expires: This header specifies the date and time after which the cached response should be considered stale.

Using message handlers, you can set these headers in the controller action methods or globally for all Web API controllers.

The CacheControl header allows you to control if the response can be cached publicly or privately and specify the max-age for caching. The Expires header sets an absolute expiration time for the cached response.

By configuring these headers appropriately, you can leverage client-side and intermediate proxy/gateway caching to improve the performance of your Web API.

 

Memory Management

80. What are the heap and the stack? Differences, principles of operation.

The heap and the stack are two different memory regions in the computer’s memory that are used to store data during the execution of a program.

The Stack

  • The stack is a memory region used for static memory allocation.
  • It is organized as a last-in, first-out (LIFO) data structure, where the last item pushed onto the stack is the first one to be popped off.
  • The stack stores function call information, such as local variables, parameters, and return addresses.
  • Memory allocation and deallocation on the stack are very fast operations.
  • The stack size is limited and determined at compile-time or during thread creation.
  • When a function is called, memory is allocated on the stack for its local variables and parameters.
  • When the function returns, the allocated memory is automatically deallocated (popped off the stack).

The Heap

  • The heap is a memory region used for dynamic memory allocation.
  • It is organized as a free pool of memory blocks that can be allocated and deallocated at runtime.
  • Memory allocation and deallocation on the heap are relatively slower operations than the stack.
  • The heap size is not fixed and can grow or shrink as needed during program execution.
  • Objects and data structures with a longer lifetime or dynamically created during runtime are stored on the heap.
  • Memory allocation on the heap explicitly uses operators like new in C#. At the same time, deallocation is usually handled automatically by the garbage collector in managed languages like C#.

The stack is used for static memory allocation and automatic memory management. In contrast, the heap is used for dynamic memory allocation and requires manual or automatic memory management (e.g., garbage collection). The stack is faster and has a limited size, while the heap is slower but can grow dynamically.

81. How does the Garbage Collector work?

The Garbage Collector (GC) is a key component of the .NET runtime. GC automatically manages the allocation and deallocation of memory for managed objects on the heap. Garbage Collector works by periodically scanning the managed heap and identifying objects that are no longer reachable, and reclaiming the memory occupied by those objects.

Here’s a general overview of how the Garbage Collector works:

  • Marking Phase: During this phase, the GC identifies the root objects (objects that are currently in use, such as static objects, objects on the stack, and objects referenced by CPU registers). It then traverses all references from these root objects, marking all reachable objects as "live."
  • Marking Phase Ending: After marking all live objects, the GC checks for sufficient memory to continue execution. If not, it proceeds to the next phase.
  • Relocating Phase: In this phase, the GC compacts the heap by moving all live objects to one end of the heap, creating a contiguous block of free memory.
  • Reclaiming Phase: After compacting the heap, the GC reclaims the memory occupied by unreachable (dead) objects, making it available for future allocations.

The GC employs various techniques and algorithms to optimize its performance. These techniques include generational garbage collection, which divides the heap into generations based on object lifetime, and background garbage collection, which allows the GC to run concurrently with the application’s threads.

82. Why do we need the using keyword in C# if .NET has automatic memory management? How is this related to the Disposable pattern, and why is such a complex pattern needed for managed and unmanaged resources?

The using keyword in C# is needed to ensure proper disposal of unmanaged resources, even with .NET’s automatic memory management for managed resources. It’s related to the IDisposable interface and the Disposable pattern, which provides a way to encapsulate the acquisition and release of unmanaged resources. This complex pattern is necessary because while the .NET Garbage Collector handles managed objects, it doesn’t manage unmanaged resources, which must be explicitly released to prevent leaks.

83. What are the peculiarities of working with the Large Object Heap?

Working with the Large Object Heap (LOH) in .NET has several peculiarities:

  • Objects larger than a certain size threshold (typically 85KB on 64-bit systems) are allocated on the LOH instead of the regular managed heap.
  • The LOH is more prone to fragmentation, as large objects are less likely to be collected and compacted regularly.
  • Objects on the LOH are not divided into generations and are not compacted as aggressively as regular objects.
  • Collecting and compacting objects on the LOH can be expensive, leading to longer garbage collection pauses.
  • Large objects on the LOH can consume significant memory, potentially causing memory pressure.
  • Finalizing large objects on the LOH is more expensive due to their size and the overhead of moving them.

Data Types, Collections, and Data Structures

84. When is a generic class of a specific type generated – at runtime or compile-time?

A generic class of a specific type is generated at compile-time through a process called code generation or specialization. When you use a generic class with a specific type argument, the compiler generates the IL (Intermediate Language) code for that specialized version of the class.

85. What is reflection?

Reflection is a mechanism in .NET that allows code to inspect and interact with itself at runtime. This mechanism provides a way to dynamically discover and manipulate types, fields, methods, properties, and other metadata of loaded assemblies during program execution.

86. Tell me about the `LinkedList<T>` collection. How does it differ from other collections?

The LinkedList<T> is a doubly-linked list collection in .NET. It differs from other collections like List<T> and arrays in that it maintains a logical order of its elements and allows efficient insertion and removal operations at any point in the list. Unlike arrays, LinkedList<T> has no need to shift elements when adding or removing items, making it more efficient for frequent modifications.

87. What is an indexer?

An indexer is a special property in C# that allows instances of a class or struct to be accessed similarly to arrays or collections using an index or a set of indices. Indexers provide a convenient syntax for accessing and manipulating data within an object, making the object behave like a virtual array or dictionary.

88. What is an immutable object? What are the benefits of using immutable objects? Propose a way to implement it in .NET.

An immutable object is an object whose state cannot be modified after creation. Once an immutable object is instantiated, its properties and fields remain constant throughout its lifetime.

Benefits of using immutable objects include:

Thread safety: Immutable objects are inherently thread-safe, as their state cannot be modified concurrently.
Simplicity: Immutable objects are simpler to reason about and debug, as their behavior is predictable and deterministic.
Caching: Immutable objects can be safely cached, as their state never changes.

To implement an immutable object in .NET, you can follow these steps:

  • Make the class immutable by removing all public setters and making all instance fields read-only or private.
  • Initialize all instance fields through the constructor or Factory methods.
  • Avoid exposing public methods that modify the object's state.
  • Return new instances instead of modifying existing ones when state changes are required.
  • Consider using read-only collections or immutable collection types for any collections the class exposes.

Following these principles, you can create immutable objects that provide thread safety, simplicity, and caching benefits in your .NET applications.

89. When should you use StringBuilder, and when should you use string? How does StringBuilder work?

You should use StringBuilder when performing multiple string operations, such as concatenations, insertions, or modifications.

string is immutable, meaning that any operation that modifies a string creates a new string object, which can lead to performance issues and increased memory usage.

StringBuilder is a mutable object that provides a more efficient way to build strings dynamically. It works by pre-allocating a buffer in memory and allowing you to modify the contents of that buffer directly without creating new string objects for each operation. This approach is more efficient, especially when dealing with large strings or performing many string operations.

Here’s a simplified overview of how StringBuilder works:

01

StringBuilder allocates an initial buffer in memory to store characters
02

As you append, insert, or modify characters, StringBuilder updates the buffer directly without creating new string objects
03

If the buffer becomes too small to hold the new characters, StringBuilder automatically resizes the buffer to accommodate the additional characters
04

When you need the final string, you can call the ToString() method, which creates a new string instance from the contents of the buffer

In general, you should use StringBuilder when performing multiple string operations, especially in loops or performance-critical code. For simple string operations or when you don’t need to modify the string, it’s fine to use the string type.

90. What is tree balancing?

Tree balancing is a technique used in self-balancing binary search trees (such as AVL trees or Red-Black trees) to ensure that the tree remains balanced and maintains its efficiency for operations like insertion, deletion, and search. A balanced tree is one where the heights of the left and right subtrees of every node differ by at most one.

Tree balancing involves performing rotations (left or right) on nodes in the tree to maintain the balance after insertions or deletions. These rotations help redistribute the nodes in the tree, ensuring that the height of the tree remains logarithmic with respect to the number of nodes, which is crucial for efficient operations.

Balancing a tree helps maintain the time complexity of operations like search, insertion, and deletion at O(log n), where n is the number of nodes in the tree. Without balancing, a binary search tree can degenerate into a linked list in the worst case, leading to linear time complexity for these operations.

91. What are key-value structures?

Key-value structures, also known as associative arrays or dictionaries, are data structures that store and organize data as key-value pairs. Each element is associated with a unique key used to access and retrieve the corresponding value in a key-value structure.

Key-value structures are widely used in various applications, such as caching systems, databases, and data processing pipelines. They provide an efficient way to store and retrieve data based on a key, which can be a primitive type (e.g., integer, string) or a custom object.

Examples of key-value structures in .NET include:

Dictionary<TKey, TValue>: A generic collection that stores key-value pairs, where the keys are unique.
Hashtable: A non-generic collection that stores key-value pairs, similar to Dictionary<TKey, TValue>.
SortedDictionary<TKey, TValue>: A sorted collection that stores key-value pairs in ascending order based on the keys.
SortedList<TKey, TValue>: A sorted collection that stores key-value pairs in a list-like structure.

Key-value structures provide efficient lookup, insertion, and deletion operations, with an average time complexity of O(1) for most operations when implemented correctly (e.g., using hash tables).

92. What is a hash function, and why are hash tables needed?

A hash function is a mathematical function that takes an input (e.g., a string, an object, or data collection) and produces a fixed-size output, typically an integer value, known as a hash code or hash value. Hash functions are designed to distribute their outputs in a uniform and pseudo-random manner across the entire range of possible hash values.

Hash tables are data structures that use hash functions to store and retrieve data efficiently. Hash tables are needed because they provide fast average-case time complexity for common operations like insertion, deletion, and lookup, which are typically O(1) on average.

Hash tables are widely used in various applications, such as caching systems, databases, compilers, and data processing pipelines, due to their efficiency in storing and retrieving data based on keys.

93. What properties should an ideal hash function have?

An ideal hash function should have the following properties:

  • Determinism: The hash function should always produce the same output for a given input. This property ensures consistency and predictability in the behavior of the hash function.
  • Uniform distribution: The hash function should distribute its outputs as uniformly as possible across the entire range of possible hash values. This property helps minimize collisions in hash tables and ensures efficient use of storage space.
  • Avalanche effect: A small change in the input should result in a significant change in the output hash value. This property helps prevent predictable patterns in the hash values and makes it harder to cause collisions intentionally.
  • Speed: The hash function should be computationally efficient and fast to calculate, as it may be called frequently in performance-critical applications.
  • Collision resistance: Finding two different inputs that produce the same hash value (collisions) should be computationally infeasible. This property is crucial for applications that rely on the uniqueness of hash values, such as cryptographic hash functions.
  • Irreversibility: It should be computationally infeasible to reconstruct the original input from the hash value alone. This property is essential for applications that use hash functions for data integrity or security purposes.

94. What are collisions, and how do you deal with them?

Collisions in hash tables occur when different keys are mapped to the same index by the hash function. Common techniques to handle collisions include separate chaining, open addressing, and rehashing.

95. What is the time complexity of CRUD operations on `Dictionary<K,V>` in .NET?

The time complexity of CRUD operations on Dictionary<K,V> in .NET is O(1) on average for retrieval, insertion, and removal, assuming a good hash function and proper load factor management.

96. Where are arrays stored? Primitive type arrays?

Arrays are stored on the managed heap in .NET. Primitive type arrays are stored as a contiguous block of memory on the managed heap.

97. What is the difference between an array (`T []`) and a list (`List<T>`)?

An array (T[]) has a fixed size that cannot be changed after creation, while a List<T> is a resizable collection that can grow or shrink dynamically.

98. What is the difference between IList<T> and IEnumerable<T>?

IList<T> is a collection that provides indexed access and modification methods, while IEnumerable<T> is a more basic interface for iterating over a collection.

99. Why do we need Enumerable, Observable, AsyncEnumerable, and what data retrieval models do they implement?

Enumerable, Observable, and AsyncEnumerable implement different data retrieval models:
Enumerable for synchronous pull-based retrieval.
Observable for push-based asynchronous retrieval.
AsyncEnumerable for asynchronous pull-based retrieval.

100. What is the difference between IEnumerable and IQueryable?

IEnumerable represents an in-memory collection, while IQueryable represents a query that can be executed against a data source (e.g., a database).

101. What are enum flags?

Enum flags are special values that can be combined using bitwise operations to represent multiple states or options simultaneously.

 

Databases

102. Explain normal forms in RDBMS.

Normal forms are rules or guidelines for designing relational database tables to minimize data redundancy and improve data integrity. The main normal forms are:

 

01

First Normal Form (1NF):

Eliminates multi-valued attributes and ensures that each cell in a table contains a single value.
02

Second Normal Form (2NF):

Meets 1NF and ensures that non-key attributes fully depend on the primary key.
03

Third Normal Form (3NF):

Meets 2NF and eliminates transitive dependencies, where non-key attributes depend on other non-key attributes.
04

Boyce-Codd Normal Form (BCNF):

Meets 3NF and ensures that every determinant is a candidate key.
05

Fourth Normal Form (4NF):

Meets BCNF and eliminates multi-valued dependencies.
06

Fifth Normal Form (5NF):

Meets 4NF and ensures that there are no redundant joins, also known as join dependencies.

Higher normal forms aim to eliminate more data redundancy and dependencies, leading to a more normalized database design.

103. What is an index in a database?

An index in a database is a data structure that stores a subset of data from a table to improve the speed of data retrieval operations. It works similarly to an index in a book, pointing to the location of specific data values within the table. Indexes are typically created on one or more columns of a table.

104. When should you use indexes? Pros and cons.

Indexes should be used when:

– Frequent queries involve searching, sorting, or retrieving data based on one or more columns.

– You need to enforce uniqueness constraints or foreign key relationships.

– You want to improve the performance of join operations between tables.

Pros of using indexes
Cons of using indexes

Faster data retrieval for queries involving indexed columns

Additional storage space is required for the index data structures

Improved query performance, especially for large tables

Due to index maintenance, increased overhead for data modification operations (inserts, updates, deletes)

Enforced uniqueness and integrity constraints

Potential for index fragmentation over time, leading to performance degradation

105. What types of indexes exist? How do they differ?

There are several types of indexes in databases:

  • Clustered Index: Physically reorders the data rows in the table based on the index key values. Only one clustered index is allowed per table.
  • Non-Clustered Index: Stores the index keys and pointers to the corresponding data rows, keeping the data rows' physical order unchanged. Multiple non-clustered indexes are allowed per table.
  • Unique Index: Ensures that the indexed column(s) have unique values across all rows in the table.
  • Composite Index: Indexes multiple columns within the same index structure.
  • Covering Index: Includes all the columns required for a query, eliminating the need to access the data rows directly.
  • Full-Text Index: Optimized for text-based searches, allowing for pattern matching and linguistic analysis.
  • Spatial Index: Used for efficiently querying and retrieving spatial data, such as geographic coordinates.

These index types differ in structure, storage mechanisms, and optimizations for various queries and data access patterns.

106. What is ACID?

ACID is an acronym that represents four essential properties of database transactions:

Atomicity: A transaction is an indivisible unit of work; either all of its operations are completed successfully or none are executed.
Consistency: A transaction must leave the database in a valid state, following all defined rules, constraints, and integrity constraints.
Isolation: Concurrent transactions must execute independently and not interfere with each other, even if they operate on the same data.
Durability: Once a transaction is committed, its effects must persist and remain permanent, even in the event of system failure or power outage.

ACID properties ensure database transactions’ reliability, integrity, and consistency, enabling applications to maintain data integrity and prevent corruption or inconsistent states.

107. What transaction isolation levels do you know?

The main transaction isolation levels in databases are:

  • Read Uncommitted: The lowest isolation level, where a transaction can read data that has been modified but not yet committed by other transactions. It can lead to dirty reads.
  • Read Committed (Default in SQL Server): A transaction can only read data that has been committed by other transactions, avoiding dirty reads. However, it is still susceptible to non-repeatable reads and phantom reads.
  • Repeatable Read: A transaction can read the same data multiple times and get consistent results, preventing non-repeatable and phantom reads.
  • Serializable: The highest isolation level, where transactions are executed as if they were happening one after another, even if they are running concurrently. This level prevents all data anomalies but may reduce concurrency and performance.

108. What is a query execution plan in MS SQL?

A query execution plan in Microsoft SQL Server is a strategy that the database engine generates to execute a given SQL query. It represents the sequence of operations and algorithms the query optimizer chooses to retrieve the requested data efficiently.

The query execution plan provides insights into the following:

  • The order of operations (e.g., table scans, index seeks, joins, sorts, aggregations)
  • The specific indexes and data structures used
  • The estimated cost and resource utilization (CPU, memory, I/O)
  • The data flow and data access patterns

By analyzing the query execution plan, database administrators and developers can:

  • Identify potential performance bottlenecks or inefficient operations
  • Optimize queries by modifying indexing strategies or query structures
  • Understand the reasoning behind the query optimizer's choices
  • Diagnose and troubleshoot slow-performing queries

109. Problem: a query is taking a long time to execute. What methods are there for diagnosing and solving this?

When a query is taking a long time to execute, there are several methods you can use to diagnose and solve the issue:

  • Analyze the Query Execution Plan: Examine the query execution plan to identify potential bottlenecks, such as inefficient index usage, costly operations (e.g., table scans), or suboptimal join strategies.
  • Check Indexing Strategy: Verify if appropriate indexes exist for the columns involved in the query predicates, joins, and sorting operations. If necessary, create new indexes or modify existing ones.
  • Review Query Structure: Analyze the query structure and consider rewriting it using more efficient constructs, such as subqueries, derived tables, or common table expressions (CTEs).
  • Check Statistics:Ensure the database statistics are up-to-date, as outdated statistics can lead to suboptimal query plans.
  • Monitor Resource Usage: Use monitoring tools to check for resource contention or bottlenecks, such as high CPU usage, memory pressure, or disk I/O issues.
  • Partition Data: If the query involves large tables, consider partitioning the data to improve query performance and enable parallel execution.
  • Optimize Database Configuration: Review database configuration settings, such as memory allocation, cost thresholds, and query optimization levels, and adjust them as needed.
  • Implement Caching: If the query involves repetitive or static data, consider implementing caching mechanisms to avoid redundant database queries.
  • Parallelize Queries: Investigate if the query can be parallelized or if the database supports parallel query execution to leverage multiple CPU cores.
  • Denormalize Data: In some cases, denormalizing data by introducing redundancy can improve query performance, but it should be carefully weighed against potential data integrity and maintenance concerns.

Often, a combination of these methods, along with profiling and iterative testing, leads to optimal query performance.

110. How do ORMs (Entity Framework or Entity Framework Core) translate C# code into database query language? What is used for this?

ORMs like Entity Framework and Entity Framework Core use a component called LINQ to Entities (LINQ to EF) to translate C# LINQ queries into database query language (SQL). LINQ to Entities acts as a query provider, taking the LINQ query expression and translating it into an expression tree. This expression tree is then processed by the Entity Framework query pipeline, which generates the corresponding SQL query to be executed against the database. The translation process involves analyzing the expression tree, applying optimization rules, and mapping the LINQ constructs to their SQL equivalents.

 

Parallelism

111. Why use Task.ConfigureAwait?

The Task.ConfigureAwait method controls the context-capturing behavior of await expressions in asynchronous methods. It is particularly important when writing code interacting with user interfaces, such as Windows Presentation Foundation (WPF) or Windows Forms applications.

When an asynchronous method is awaited without ConfigureAwait, the continuation (code after the await) will capture and resume on the same context (e.g., UI thread) that triggered the asynchronous operation. It can lead to performance issues and potential deadlocks if the awaited task is not related to the UI thread’s work.

By calling ConfigureAwait(false), you instruct the asynchronous method to resume its continuation on a different context (e.g., a thread pool thread) rather than capturing the original context. It helps to prevent unnecessary context switches and improves performance, especially in scenarios where the awaited task does not require the UI thread’s context.

In summary, using Task.ConfigureAwait(false) is recommended in non-UI asynchronous methods to avoid unnecessarily capturing the UI thread’s context, which can lead to performance issues and deadlocks.

112. For example, a web server performs a database query for an HTTP request. The server has 16 threads in total. Each HTTP request executes a database query and waits for the results, blocking the thread. Can this be optimized using .NET means?

This scenario can be optimized using asynchronous programming in .NET. Instead of blocking threads waiting for database queries, use async/await with asynchronous database operations, allowing threads to be released back to the thread pool while waiting. Also, consider increasing the maximum thread pool size if expecting high concurrency.

113. Why is the ThreadPool needed? Describe the mechanics: how a thread is allocated and returned to the ThreadPool.

The ThreadPool provides an efficient way to manage and reuse threads for asynchronous/parallel tasks, avoiding the overhead of manual thread creation/termination. When a task is requested, the ThreadPool allocates an available thread from its queue; if none is available and under the max limit, it creates a new thread. After executing the task, the thread is returned to the queue for reuse until an idle timeout, then terminated.

.NET Interview Questions for Senior Developers

For senior .NET developer interviews, initial rounds screen in-depth .NET frameworks knowledge and design patterns. Technical rounds involve architecting complex systems, optimizing performance, and answering senior .NET developer interview questions. Candidates often solve distributed system design and database scalability problems.

 

General .NET Interview Questions

114. What other practices besides OOP have you used (AOP, FP, etc.)?

Besides OOP, Aspect-Oriented Programming (AOP) is used to cross-cut concerns.
Functional Programming (FP) concepts like LINQ and lambda expressions.
Event-Driven Programming for loosely coupled systems.
Reactive Programming with Reactive Extensions (Rx).
Domain-Driven Design (DDD) for modeling complex domains.

115. Name three of the most complex problems you’ve had to solve. How did you solve them, and how did you arrive at that solution?

Complex problems include implementing a distributed caching system (used Redis, data partitioning, failover), optimizing a data-intensive ETL process (parallelization, performance tuning), and developing a real-time messaging system (used message queues, load balancing, failover). Solutions involved researching technologies, prototyping, performance testing, and iterative design.

116. What is loosely-coupled code? How is it better than tightly-coupled code? How would you achieve loose coupling?

Loosely-coupled code has minimal dependencies between components, enabling independent development and evolution. It’s better than tightly-coupled code, which is harder to maintain and change. Achieve loose coupling through abstractions (interfaces), dependency injection, event-driven communication, and modular design.

117. Does using static classes increase or decrease code coupling?

Static classes can increase code coupling by introducing global states and dependencies across the codebase. Static classes are best used judiciously for utility classes or when truly required.

118. How can you measure code performance? Does the act of measuring affect performance?

Code performance can be measured using profiling tools, benchmarking frameworks, and performance counters. The act of measuring can affect performance due to instrumentation overhead.

119. What are multi-stage builds in Docker, and how do they work?

Multi-stage builds in Docker are a feature that allows you to build your Docker image in multiple stages. It is useful for reducing the final image size by only including the necessary files and dependencies. Each stage can use a different base image, and you can copy artifacts from one stage to the next.

120. How can you tell when a piece of code is consuming a lot of memory or running slowly? What can be a bottleneck in different cases? What are ways to reduce memory usage and memory traffic?

You can use profiling tools like memory and CPU profilers to identify code that consumes a lot of memory or runs slowly. Potential bottlenecks could be CPU-bound tasks, I/O-bound tasks, or memory-intensive operations. You can optimize data structures, limit allocations, and use techniques like memory pooling to reduce memory usage. To reduce memory traffic, you can improve cache utilization, minimize data copying, and optimize memory access patterns.

121. How would you implement a cross-cutting concern (e.g., logging, validation, transactions)?

You can use design patterns like the Decorator or Aspect-Oriented Programming (AOP) approach to implement cross-cutting concerns like logging, validation, or transactions.

122. Tell me about the REST Maturity Model.

The REST Maturity Model is a framework for evaluating the level of “RESTfulness” in an API. It has four levels: Level 0 (the most basic), Level 1 (resource-oriented), Level 2 (HTTP verbs), and Level 3 (hypermedia controls).

123. What are CPU-bound and IO-bound tasks?

CPU-bound tasks, such as mathematical computations, primarily use the CPU. while
I/O-bound tasks involve waiting for external resources, such as reading from a database or making network requests.

124. What is marshaling?

Marshaling is the process of converting an object’s internal representation into a format that can be transmitted, stored, or read from a stream.

125. How does async/await work (in detail)? Why can’t you use async void methods?

Async/await in programming languages like C# and JavaScript allows developers to write asynchronous code that appears synchronous. The await keyword pauses the execution of the current method until the asynchronous operation is complete, allowing the thread to be used for other tasks in the meantime. Async void methods are not recommended because they can be difficult to handle errors and exceptions.

 

.NET Framework Interview Questions

126. How does the lock statement work? Can you use structs inside a lock expression?

The lock statement in C# acquires a mutual exclusion lock on a given object, ensuring that only one thread can execute the code within the lock block at a time. Structs can be used inside a lock expression, but it’s generally not recommended as structs are value types and can lead to unexpected behavior.

127. What are Expression Trees?

Expression Trees represent code in a tree-like data structure, where each node represents a particular code construct, such as a method call, a variable, or an operator. They are used in LINQ and other advanced .NET features to analyze and manipulate code at runtime.

128. How does the Garbage Collector work (in detail)? Why are there three generations in GC, not five, ten, or two?

The .NET Garbage Collector (GC) automatically reclaims memory occupied by objects no longer in use. GC uses a generational approach, with three generations (0, 1, and 2), to optimize performance. Newer objects are placed in generation 0, and if they survive multiple collections, they are promoted to higher generations. This approach collects the most frequently used, short-lived objects more often.

129. How would you set up tracing for Web API services?

You can use tools like Application Insights or Elastic APM to set up tracing for Web API services. These provide out-of-the-box integration with ASP.NET Core and allow you to collect and analyze various telemetry data.

130. How can you configure secret storage for .NET Core applications on developer machines and production environments?

For secret storage in .NET Core applications, you can use solutions like Azure Key Vault, AWS Secrets Manager, or HashiCorp Vault. These provide a secure way to store and manage sensitive information in development and production environments, such as connection strings and API keys.

131. How would you organize the CI/CD process for .NET Core services to deploy them to a cloud infrastructure?

To organize the CI/CD process for .NET Core services, you can use tools like Azure DevOps, GitHub Actions, or Jenkins. These allow you to set up automated build, test, and deployment pipelines, ensuring consistent and reliable deployments to your cloud infrastructure.

132. How do you enable CORS in ASP.NET Core?

In ASP.NET Core, you can enable CORS (Cross-Origin Resource Sharing) by configuring the CorsOptions in the Startup.cs file. It allows you to specify which origins, headers, and HTTP methods are allowed to access your API.

 

Data Types, Collections, and Data Structures

133. How are generics implemented?

Generics in .NET are implemented using a technique called “type erasure.” During compilation, the generic type parameters are replaced with their constrained types, and the resulting code is then compiled as if the generic types were not present.

134. How do you create your immutable type?

To create your immutable type, you can make the class sealed and ensure that all its properties are read-only. You can also make the class’s constructor private and provide a static factory method to create instances of the class.

135. How does IEnumerable<T> work (in detail)?

IEnumerable<T> is a simple interface that defines a single method, GetEnumerator(), which returns an IEnumerator<T>. This interface allows you to iterate over a collection of items without exposing the underlying data structure.

136. What algorithm does the STACK collection use?

The STACK collection uses a Last-In-First-Out (LIFO) algorithm, where elements are added and removed from the top of the stack.

137. What data structures have you implemented yourself for the .NET platform? How did they differ from the standard implementations?

I have implemented several custom data structures for the .NET platform, including a custom Linked List and a Priority Queue. These implementations often differ from the standard .NET collections regarding performance characteristics, memory usage, or specialized functionality.

138. How do interfaces differ from abstract classes? In what cases would you use one or the other?

The main difference between interfaces and abstract classes is that interfaces can only define method signatures, properties, events, and indexers. At the same time, abstract classes can include method implementations, fields, and constructors. Interfaces are generally used to define contracts or APIs, while abstract classes are used to provide a base implementation for related types.

139. Why don’t structs have a default constructor?

Structs in .NET do not have a default constructor because they are value types, and value types are initialized with their default values (e.g., 0 for numeric types, false for bool, null for reference types) when created. Providing a default constructor for a struct would be redundant and could lead to unexpected behavior.

 

Databases

140. How do databases store data?

Databases store data in a structured way, typically in tables with rows and columns. The specific storage mechanisms vary between different database management systems (DBMS). However, they generally use a combination of in-memory caching, disk-based storage, and indexing to optimize performance.

141. What types of databases do you know?

The main types of databases include Relational Database Management Systems (RDBMS), such as SQL Server, PostgreSQL, and MySQL, as well as NoSQL databases like MongoDB, Cassandra, and Redis.

142. When and which databases are better to use?

The choice of database depends on factors like the data model, scalability requirements, performance needs, and the type of queries the application will perform. RDBMS are well-suited for applications with structured data and complex relationships. In contrast, NoSQL databases excel at handling unstructured data and high-volume, high-velocity workloads.

143. What is denormalization?

Denormalization is the process of intentionally duplicating data in a database to improve query performance.

144. When and which transaction isolation levels can you use?

Transaction isolation levels determine how much interference can occur between concurrent transactions. The most common levels are READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, and SERIALIZABLE, each with different trade-offs between consistency and concurrency.

145. How are the ACID principles implemented in popular RDBMSs (SQL Server, PostgreSQL, etc.)?

ACID (Atomicity, Consistency, Isolation, Durability) principles are implemented in RDBMS through features like transaction management, locking mechanisms, and logging/recovery processes.

146. Have you ever optimized a database query? If so, how?

Database queries can be optimized by analyzing execution plans, adding appropriate indexes, and denormalizing data structures.

147. Describe potential issues you know of related to parallel database queries.

Potential issues with parallel database queries include deadlocks, race conditions, and increased contention for shared resources. These issues can lead to performance degradation or data consistency issues.

148. Which database would you use to implement a distributed lock mechanism? Discuss the implementation details.

A distributed lock mechanism can be implemented using a database that supports distributed transactions, such as SQL Server with Distributed Transaction Coordinator (DTC) or PostgreSQL with pg_dist_lock. The implementation would involve creating a table to store lock information and using transactions to acquire, release, and manage the locks.

 

Microservices

149. What problem do microservices solve?

Microservices solve the complexity problem in large, monolithic applications by breaking them down into smaller, independent services that can be developed, deployed, and scaled separately.

150. What are the ways microservices can communicate?

Microservices can communicate using various inter-service communication patterns, such as synchronous HTTP/REST, asynchronous messaging (e.g., queues, event buses), or a hybrid approach.

151. Discuss implementation options for distributed transactions in microservices.

Distributed transactions in microservices can be implemented using techniques like the Saga pattern. It coordinates multiple local transactions to achieve eventual consistency or by using a distributed transaction coordinator (DTC) like the one provided by SQL Server.

152. What is a circuit breaker?

A circuit breaker is a design pattern that helps manage failures in distributed systems by preventing cascading failures and providing fallback options when a downstream service is unavailable.

153. How would you debug a system composed of multiple microservices to trace the full request processing path?

To debug a microservices-based system, you can use distributed tracing tools like Jaeger or Zipkin, which allow you to trace the full request processing path across multiple services.

154. What are message brokers? What are at-least-once, at-most-once semantics? Are there any brokers that guarantee exactly-once semantics?

Message brokers like RabbitMQ, Apache Kafka, or Azure Service Bus facilitate asynchronous communication between microservices. At-least-once semantics ensure that a message is delivered at least once, while at-most-once semantics guarantee that a message is delivered at most once. Exactly-once semantics, which ensures a message is delivered exactly once, can be achieved using techniques like idempotency.

155. How should the broker client code work depending on the chosen semantics?

Depending on the chosen message delivery semantics, the broker client code should appropriately handle retries, timeouts, and error handling.

156. What queue-related tools are you familiar with (both in .NET and standalone products), and which would you choose and why?

I’m familiar with various queue-related tools, such as RabbitMQ, Apache Kafka, and Azure Service Bus. The choice would depend on factors like the application’s specific requirements, the team’s familiarity with the technology, and the overall ecosystem.

 

MS Azure Interview Questions

157. What types of services are available in Service Fabric?

Azure Service Fabric provides several types of services, including Stateless Services, Stateful Services, and Actor-based services. Stateless services are simple, scalable services without any persistent state, while Stateful services maintain their state. Actor-based services are a programming model that simplifies the development of highly scalable and reliable distributed applications.

158. What are the characteristics and limitations of Azure Table Storage?

Azure Table Storage is a NoSQL key-value store with high availability, durability, and scalability. Its key characteristics include eventual consistency, a limited query capability compared to SQL databases, and a maximum table size of 500 TB. It is well-suited for storing large amounts of semi-structured data, such as user profiles, device information, and log data.

159. How do you deal with the cold start problem in Azure Functions?

The cold start problem in Azure Functions refers to the delay in function execution when the runtime needs to spin up a new instance to handle a request. To mitigate this, you can use strategies like keeping the function app warm, using a consumption plan with a pre-warmed instance, or leveraging a dedicated App Service plan.

160. What’s the difference between queues and topics in Azure Service Bus?

In Azure Service Bus, queues deliver simple first-in, first-out (FIFO) message. At the same time, topics enable a publish-subscribe model where multiple subscriber rules can be defined to filter messages. Queues are useful for one-to-one communication, while topics are better suited for one-to-many scenarios.

Summing up

Acing a .NET interview requires a collaborative effort. Candidates need to demonstrate their technical expertise, problem-solving skills, and passion for development. However, for hiring managers, a successful interview goes beyond a list of .NET interview questions. Evaluate communication skills and cultural fit, ensuring the candidate thrives in a team. Give coding challenges using .NET in interviews. Observe their approaches and logic.

Thankfully, valuable solutions are available to simplify this process. If you want to skip the hassle of screening and interviewing, contact us. Here at DOIT Software, we have a smooth process for hiring top .NET developers. We can provide you with the first CVs in a few days.

Frequently Asked Questions

Planning to hire .NET developers?

Get a consultation and start building your dream team ASAP.

Request CVs

How do I prepare for a .NET interview?

Cracking a .NET interview boils down to three key steps. First, brush up on the basics – C#, how memory is managed (garbage collection), and the .NET libraries you might use. Second, practice answering common .NET interview questions you can find in the list above. Finally, sharpen your problem-solving skills – interviewers love to see how you think through coding challenges.

How do I prepare for a senior .NET developer interview?

Acing a senior .NET interview hinges on three things: 1) Level up your skills with advanced topics like ASP.NET Core MVC and design patterns. 2) Showcase your experience with real-world projects, highlighting challenges and impact. 3) Think leader – be ready to discuss code reviews, knowledge sharing, and building a strong team.

What is .NET Framework in C# interview questions?

.NET Framework is Microsoft’s software framework that provides the runtime environment and core class libraries for building applications using C#. C# code compiles to an intermediate bytecode executed by the .NET runtime (CLR). The .NET Framework handles memory management, security, and other system services for C# apps. It offers extensive class libraries for desktop, web, and mobile development. .NET allows C# code to use libraries written in other .NET languages.

Vitaly DOIT Software
Vitalii Makhov
CEO @ DOIT Software
Please rate the article
  • star empty star full
  • star empty star full
  • star empty star full
  • star empty star full
  • star empty star full
4.72/5
Based on 5 reviews

Read More About