C#/.NET Flashcards
Key Concepts of Array( T[] )
- Fixed Size: Once declared, the size cannot change.
- Zero-Based Indexing: The first element is at index 0.
- Strongly Typed: All elements must be of the same type.
- Stored in Contiguous Memory: Improves performance for iteration.
- Fast Access- Accessing an element by index O(1)
- O(1) = constant time complexity - meaning an operation takes the same amount of time to complete, regardless of array size.
Key Concepts List<T></T>
- Dynamic Size: Can grow and shrink dynamically.
- Generic Collection: Stores elements of a specific Type <T>.</T>
- Uses and Array Internally: Expands size when needed by allocating a new larger array.
- Index-Based Access: Fast loop up O(1) like arrays.
- Part of System.Collection.Generic: Must import by using System.Collection.Generic.
Dictionary<TKey, TValue>
- Key-Value Collection: Stores Data as key-value pairs TKey -> TValue.
- Fast Lookup O(1): Uses a hash table for quick retrieval.
- Unique Keys: Each key must be unique.
- Dynamic Resizing: Expands automatically when needed.
- Imported by: using System.Collection.Generic.
IEnumerable<T></T>
- Interface for Iteration: Allows sequential access to a collection.
- Supports foreach Loop: Used to iterate over elements.
- Lazy Evaluation: Can return elements as needed (useful for large datasets).
- Read-Only: Does not support adding/removing items.
- Found in System.Collections.Generic.
LINQ Method .Where(predicate)
*predicate(Func<T, bool>) - A function that returns true or false.
Filters Collection
LINQ Method .Select(selector)
*selector (Func<T, TResult>) - A function that transforms elements in a collection.
Projects each element into a new form.
LINQ Method .OrderBy(keySelector)
*keySelector(Func<T, TKey>) - A function that extracts a key for sorting, grouping, or look up.
Sorts in Ascending Order.
LINQ Method .OrderByDescending(keySelector)
*keySelector(Func<T, TKey>) - A function that extracts a key for sorting, grouping, or look up.
Sorts in Descending Order.
LINQ Method .GroupBy(keySelector)
*keySelector(Func<T, TKey>) - A function that extracts a key for sorting, grouping, or look up.
Groups Elements by Key.
LINQ Method .FirstOrDefault() .SingleOrDefault()
Retrieve single elements safely.
Define Value types (e.g., int, bool, char, structs, enums)
Value Types: are stored on the stack. When you assign a value type variable to another, it copies the value.
Define Reference types (e.g., class, interface, string, object)
Reference types: are stored on the heap. When assigned to another variable, only a reference (memory address) is copied, not the actual object.
Garbage Collection in .NET:
.NET has an automatic garbage collector (GC) that cleans up unused memory. The GC runs in generations: explain the generations.
Gen 0 – Short-lived objects (most objects start here).
Gen 1 – Longer-lived objects that survived Gen 0 collection.
Gen 2 – Long-lived objects (e.g., static data, application-wide objects).
Stack Memory
Used for: Storing value types (int, double, struct, etc.) and method call info.
Fast because it follows a Last In, First Out (LIFO) structure—like stacking plates.
Automatically managed → When a method finishes, everything on the stack for that method is wiped away.
Heap Memory
Used for: Storing reference types (class objects, arrays, interfaces, etc.).
Slower than stack because objects stay in memory until garbage collected.
Dynamically allocated → Objects can grow and live beyond a method call.
OOP: Encapsulation (Data Hiding)
The idea of hiding implementation details and exposing only necessary parts of a class.
Achieved using access modifiers (private, public, protected, internal).
Helps prevent accidental modification of data.
OOP: Inheritance
Inheritance allows a child (derived) class to inherit properties, methods, and behavior from a parent (base) class.
Promotes code reusability (avoid duplicating code).
Helps with maintainability (changes in the base class apply to derived classes).
Supports polymorphism, which we’ll cover next.
What happens if the base class has a method and the child class defines a method with the same name?
(Hint: Think about method overriding.)
If the base method is virtual, the child class can override it using override.
If the base method is not virtual, the child class can hide it using new, but it’s not a true override.
override in the child class changes the method’s behavior
What is method overloading, and how is it different from method overriding?
Method Overloading → Same method name but different signatures (parameters, types, or order). The correct method is chosen at compile-time.
Method Overriding → A child class modifies the behavior of a method from the parent class using override. The correct method is chosen at runtime.
Use overloading (compile-time) when different parameters define different behaviors (e.g., payments, string formatting).
Use overriding (runtime) when different objects define different behaviors (e.g., notifications, UI rendering).
Difference between Abstract and Interface?
Abstract Class → Used when classes share common behavior but also need some unimplemented methods (abstract methods).
Interface → Acts like a contract or template, forcing unrelated classes to follow the same method signatures without providing any shared implementation.
What is Dependency Injection?
Dependency Injection (DI) is a design pattern that helps achieve loose coupling between components by injecting dependencies instead of creating them inside a class.
🔹 Instead of a class creating its own dependencies, they are passed (injected) into the class from the outside.
🔹 Why use DI?
✅ Makes code more maintainable & testable.
✅ Reduces tight coupling between classes.
✅ Helps follow SOLID principles (especially the D - Dependency Inversion Principle).
Why do we use an interface like INotificationSender instead of a concrete class like EmailService in Dependency Injection?
“Using an interface allows for loosely coupled code through abstraction. It makes the code more modular and dynamic, which improves maintainability, makes it easier to test, and allows us to extend functionality without changing existing logic.”
What are the benefits of using constructor injection over creating the dependency directly inside the class? (Name at least 2)
With constructor injection, we inject dependencies from the outside, which promotes code reusability, testability, and loose coupling. This allows the class to work with any implementation of an abstraction (like an interface), making it easier to switch or extend functionality without modifying the class.
On the other hand, if you create the dependency directly inside the class, the code becomes tightly coupled, harder to test, and less flexible. You’d have to rewrite or duplicate logic if you wanted to use a different implementation.
What does this line in the constructor do?
_notificationSender = notificationSender;
The line _notificationSender = notificationSender; is assigning the value from the constructor parameter (notificationSender) to the class’s private field (_notificationSender).
The underscore is a naming convention indicating that the field is private, and storing the parameter this way ensures the class can reuse that dependency later in its methods.
What is a Controller & what does it do?
A controller in ASP.NET Core Web API is the entry point that handles incoming HTTP requests from clients. It decides what action to take by calling the appropriate service, and then returns a response—usually as JSON.
Controllers should not contain business logic themselves—they delegate that responsibility to services.
What is ASP.NET Core?
ASP.NET Core is a free, open-source web framework built by Microsoft for creating web apps, APIs, and services using C#. It runs on .NET Core, which is cross-platform and high-performance.
Key Benefits of ASP.NET Core:
Cross-platform (Windows, Mac, Linux)
Fast and modern
Built-in dependency injection
Middleware pipeline (you can customize request/response flow)
Cloud-ready and lightweight
Explain the 3 lifetimes ASP.NET Core has:
AddScoped - New instance per request (most common for APIs)
AddSingleton - One instance shared for the app’s entire lifetime
AddTransient - New instance every time it’s requested
.NET Unit testing what are each used for: xUnit, Moq, Assert
xUnit (most common framework)
Moq (for mocking dependencies)
Assert (to check expected vs actual results)