Fundamentals
C# Versions
C# 1.0 (Released 2002)
- Key Features:
- Classes
- Structs
- Interfaces
- Events
- Properties
- Delegates
C# 2.0 (Released 2005)
- Key Features:
- Generics
- Partial Classes
- Static Classes
- Nullable Types
- Co-variance and Contravariance
C# 3.0 (Released 2007)
- Key Features:
- LINQ (Language Integrated Query)
- Lambda Expressions
- Extension Methods
- Anonymous Types
- Implicitly Typed Variables (using
var
) - Expression Trees
C# 4.0 (Released 2010)
- Key Features:
- Dynamic Binding (using
dynamic
) - Named Arguments
- Optional Parameters
- Improved COM Interoperability
C# 5.0 (Released 2012)
- Key Features:
- Async and Await for asynchronous programming
- Caller Information Attributes
C# 6.0 (Released 2015)
- Key Features:
- String Interpolation
- Null-Conditional Operators
- Expression-bodied Members
- Auto-Property Initializers
- Static Imports
C# 7.0 (Released 2017)
- Key Features:
- Tuples
- Pattern Matching
- Local Functions
- Ref locals and returns
- Out Variables
C# 7.1 (Released 2017)
- Key Features:
- Default Literal
- Async Main method
- Language Versioning
C# 7.2 (Released 2017)
- Key Features:
- Non-constant default values
- Enhanced performance for safe code
C# 7.3 (Released 2018)
- Key Features:
- Improved pattern matching
- New compiler options
C# 8.0 (Released 2019)
- Key Features:
- Nullable Reference Types
- Default Interface Methods
- Switch Expressions
- Using Declarations
- Asynchronous Streams
C# 9.0 (Released 2020)
- Key Features:
- Records (for immutable data)
- Init-only Properties
- Top-level Statements
- Pattern Matching Enhancements
C# 10.0 (Released 2021)
- Key Features:
- Global usings: Use of the
global
keyword to apply using directives across all files. - Null parameter checking: Simplifies null checks for parameters.
- Constant interpolated strings: Allows constant strings to be initialized using interpolation.
- Record structs: Introduces value-type records for immutable data structures.
- Extended property patterns: Simplifies accessing nested properties in patterns.
- Top-level statements: Allows writing code without the need for a
Main
method in simple applications. - Lambda improvements: Enhancements to lambda expressions for better usability.
C# 11.0 (Released 2022)
- Key Features:
- Raw string literals: Simplifies multi-line string handling without escaping.
- Required members: Allows specifying that certain properties must be initialized during object creation.
- Generic math: Introduces support for mathematical operations on generic types.
- New pattern matching enhancements: Includes improvements for switch expressions and pattern matching capabilities.
- Improvements to records: Enhancements to record types for better functionality and usability.
C# 12.0 (Released November 2023)
- Key Features:
- Primary Constructors
- Collection Expressions
- Inline Arrays
- Optional Parameters in Lambda Expressions
- Alias Any Type
- Default interface methods: Allows interfaces to have default implementations.
- New language features for pattern matching: Further enhancements to pattern matching capabilities.
- Improvements to async/await: Enhancements for better handling of asynchronous programming.
- Static abstract members in interfaces: Allows interfaces to define static members.
C# 13.0 (November 2024)
- Enhanced params collections: The
params
keyword now works with any collection type.
- New lock type: Introduces
System.Threading.Lock
for better thread synchronization.
- Partial properties: Allows separating property declaration from implementation.
- New escape sequence: Introduces
\e
for the escape character.
- Implicit indexer access: Enables the use of indexers directly within object initializers.
Operators
Operator Category | Operators |
Arithmetic Operators | + , - , * , / , % , ++ , -- |
Relational Operators | == , != , > , < , >= , <= |
Logical Operators | && , ` |
Bitwise Operators | & , ` |
Assignment Operators | = , += , -= , *= , /= , %= , <<= , >>= , &= , ^= , ` |
Type Testing Operators | is , as |
Lambda Operator | => |
Member Access Operators | . (dot operator for accessing members) |
Indexers | [] (used to access elements in arrays or collections) |
Pointer Operators | * , & |
Operator | Description | Example Usage |
Range Operator ( .. ) | Used to create a range of values. It can be used with arrays and collections to specify a range of indices. | var range = 0..5; (creates a range from 0 to 4) |
Index Operator ( ^ ) | Used to specify an index from the end of a collection. It allows you to access elements from the end without calculating the index. | var lastElement = array[^1]; (accesses the last element) |
Null Coalescing Operator ( ?? ) | Returns the left-hand operand if it is not null; otherwise, it evaluates and returns the right-hand operand. | string name = possiblyNullName ?? "Default Name"; |
Null Coalescing Assignment Operator ( ??= ) | Assigns the right-hand operand to the left-hand operand only if the left-hand operand is null. | someValue ??= defaultValue; |
is Operator | Used for type checking. It evaluates to true if the operand is of the specified type or a derived type. | if (obj is string) { ... } |
as Operator | Used for safe casting. It attempts to cast the operand to the specified type and returns null if the cast fails. | string str = obj as string; |
sizeof Operator | Returns the size of a data type in bytes. | int size = sizeof(int); |
typeof Operator | Returns the System.Type object representing the operand's type. | Type t = typeof(string); |
nameof Operator | Returns the name of a variable, type, or member as a string. | string name = nameof(myVariable); |
Conditional Operator ( ?: ) | The ternary operator. It evaluates an expression and returns one of two values depending on whether the expression evaluates to true or false. | int result = (a > b) ? a : b; |
Type Conversions in C#
1. Implicit Type Conversion
Implicit type conversion happens automatically by the compiler when the destination type is larger than the source type. This is safe because no data will be lost during the conversion.Examples of implicit conversions:
- Converting
int
tolong
,float
,double
, ordecimal
- Converting
char
toint
- Converting
byte
orshort
toint
2. Explicit Type Conversion (Type Casting)
Explicit type conversion requires a cast operator and is used when converting to a type that has a different representation. This can result in a loss of data or an invalid representation.Examples of explicit conversions:
- Converting
double
toint
- Converting
char
tobyte
- Converting
int
tochar
3. Boxing
Boxing is the process of converting a value type (such as
int
, float
, char
, etc.) to a reference type (specifically, to an object
). Boxing can incur a performance overhead because it involves allocating memory on the heap and copying the value from the stack to the heap.csharpint number = 123;
// Value type
object boxedNumber = number;
// Boxing
4. Unboxing
Unboxing is the reverse process of boxing. It involves converting a reference type (specifically, an
object
) back to a value type. Unboxing requires an explicit cast. Unboxing also incurs a performance cost, as it involves checking the type of the object and copying the value back to the stack.csharpobject boxedNumber = 123;
// Boxing
int number = (int)boxedNumber;
// Unboxing
Data Types in C#
- Primitive - byte, sbyte, short, ushort, int, uint, long, ulong, float, double, decimal, char, bool
- Non-primitive - string, object, class, interface, array, delegate, dynamic
What are the 2 types of data types available in C#?
- Value Types - Structs, Primitives, Enums
- Reference Types - Non-primitive
Feature | Value Types | Reference Types |
Storage Location | Stored on the stack | Stored on the heap |
Data Storage | Contains the actual value | Contains a reference to the data |
Memory Allocation | Allocated at compile time | Allocated at runtime |
Default Value | Cannot be null (default is zero) | Can be null |
Immutability | Generally immutable | Can be mutable |
Comparison | Compared by value | Compared by reference |
Examples | int , float , char , bool | string , object , class , array |
Performance | Generally faster due to stack allocation | Slower due to heap allocation |
Inheritance | Cannot inherit from other types (sealed) | Can inherit from other classes |
Constructors in C#
Constructors in C# are special methods that are automatically called when an object of a class is created. They are used to initialize the state of an object by setting the values of its fields. Here are the key points about constructors in C#:
Definition and Syntax
- A constructor has the same name as the class it belongs to.
- It does not have a return type, not even
void
.
- The constructor's method signature includes only an optional access modifier and the parameter list.
public class Person { private string name; public Person(string name) { this.name = name; } }
Types of Constructors
- Default Constructor: A constructor with no parameters that initializes the object with default values. If no constructor is defined, C# provides a default constructor that initializes fields to their default values (0 for numbers,
null
for references).
- Parameterized Constructor: A constructor that takes one or more parameters to initialize the object with specific values.
- Copy Constructor: A constructor that creates a new object by copying the values of an existing object of the same type.
- Static Constructor: A constructor that initializes the static fields of a class. It is called automatically before the class is used for the first time.
- Private Constructor: A private constructor in C# is a special instance constructor that is declared with the ‘private’ access modifier. It can only be called within the class itself and is often used for specific purposes, such as implementing design patterns like the Singleton pattern or providing controlled instance creation.
Characteristics
- A class can have multiple constructors with different parameter lists (constructor overloading).
- Constructors cannot be
abstract
,virtual
, orstatic
(except for static constructors).
- Constructors cannot have the same name as any instance method in the class.
- Constructors can have access modifiers to control their visibility.
Abstract Class vs Interface
Abstract classes and interfaces in C# are both essential tools for implementing abstraction and polymorphism, but they serve different purposes and have distinct characteristics. Below is a detailed comparison of the two.
Key Similarities
- Define Behavior Without Implementation: Both abstract classes and interfaces allow you to define behavior that must be implemented by derived classes or implementing classes.
- Cannot Be Instantiated: Neither abstract classes nor interfaces can be instantiated directly.
- Contain Abstract Methods: Both can declare methods that must be implemented by derived classes or implementing classes.
Key Differences
Feature | Abstract Class | Interface |
Instantiation | Cannot be instantiated | Cannot be instantiated |
Inheritance | A class can inherit from only one abstract class | A class can implement multiple interfaces |
Method Implementation | Can contain both abstract and concrete methods | Can only contain method signatures (until C# 8.0, where default implementations were introduced) |
Fields | Can contain fields | Cannot contain fields; can only declare properties, methods, events, and indexers |
Constructors | Can have constructors | Cannot have constructors |
Access Modifiers | Can have access modifiers (public, protected, etc.) | Members are public by default |
State Management | Can maintain state through fields | Cannot maintain state |
Use Cases | Suitable for closely related classes with common functionality | Suitable for defining a contract for unrelated classes |
Adding New Members | Easier to add new methods without breaking existing implementations | Adding new methods can break existing implementations unless default implementations are used (C# 8.0 and later) |
When to Use Each
- Abstract Class: Use when you want to provide a common base class with some shared functionality. It is ideal when you have a class hierarchy where derived classes share common behavior or state.
- Interface: Use when you want to define a contract that can be implemented by multiple classes, especially when those classes are not related. Interfaces are more flexible as they allow a class to implement multiple interfaces.
Example Scenarios
- Abstract Class Example:
public abstract class Animal
{
public abstract void Speak();
public void Sleep()
{
Console.WriteLine("Sleeping...");
}
}
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Bark");
}
}
- Interface Example:
public interface IDamageable
{
void TakeDamage(int amount);
}
public class Player : IDamageable
{
public void TakeDamage(int amount)
{
Console.WriteLine($"Player takes {amount} damage.");
}
}
Field and Property
Feature | Field | Property |
Syntax | type fieldName; | type PropertyName { get; set; } |
Accessibility | Same accessibility for reading and writing | Getter and setter can have different accessibility |
Encapsulation | Directly exposes the underlying variable | Can add extra logic and validation |
Memory Footprint | Increases the memory footprint proportionally | Does not increase the memory footprint |
Interfaces | Cannot be declared in interfaces | Can be part of an interface contract |
Initialization | Can be initialized inline or in constructor | Can be initialized inline or in constructor |
Backing Field | No backing field is required | Typically has a private backing field |
Naming Convention | Use camelCase or PascalCase | Use PascalCase |
Use Case | Useful for internal implementation details | Provides a flexible way to expose class data |
Types of classes
Class Type | Description | Example Syntax |
Regular Class | The most common type of class that can be instantiated and can contain methods, properties, and fields. | public class MyClass { } |
Abstract Class | A class that cannot be instantiated and is meant to be a base class for other classes. It can contain abstract methods (without implementation) and concrete methods. | public abstract class MyBaseClass { public abstract void MyMethod(); } |
Sealed Class | A class that cannot be inherited. This is used to prevent further derivation from the class. | public sealed class MySealedClass { } |
Static Class | A class that cannot be instantiated and can only contain static members. It is used for utility or helper methods. | public static class MyStaticClass { public static void MyMethod() { } } |
Partial Class | A class that can be split into multiple files. This allows for better organization of code and separation of concerns. | partial class MyPartialClass { } |
Nested Class | A class defined within another class. It can be used to logically group classes that are only used in one place. | public class OuterClass { public class InnerClass { } } |
Equality Operator(==) and Equals()
Feature | == Operator | Equals() Method |
Type | Operator | Method |
Comparison Type | Compares reference identity for reference types; compares values for value types | Compares content of objects, can be overridden |
Default Behavior | For reference types, checks if both references point to the same object in memory | Checks if the contents of the objects are equal |
Null Handling | Can handle null values without throwing exceptions | Throws a NullReferenceException if called on a null object |
Overloading | Can be overloaded in user-defined types | Can be overridden in user-defined types |
Performance | Generally faster for reference types due to direct comparison | Slightly slower due to method call overhead |
Use Cases | Use for checking if two references point to the same object or for value types | Use for checking logical equality of object contents |
Example with Strings | string str1 = "Hello"; string str2 = "Hello"; str1 == str2; // true | str1.Equals(str2); // true |
Example with Reference Types | object obj1 = new object(); object obj2 = new object(); obj1 == obj2; // false | obj1.Equals(obj2); // false |
Difference b/w Const vs ReadOnly vs Static ReadOnly
Feature | const | readonly | static readonly |
Definition | A constant field whose value is set at compile time and cannot be changed. | A field that can only be assigned during declaration or in a constructor. | A static field that can only be assigned during declaration or in a static constructor. |
Initialization | Must be initialized at the time of declaration. | Can be initialized at declaration or in any constructor of the class. | Can be initialized at declaration or in a static constructor. |
Scope | Implicitly static; belongs to the type itself. | Instance-level; belongs to an instance of the class. | Static; belongs to the type itself. |
Value Change | Cannot be changed after initialization. | Can be assigned a value only once, either at declaration or in a constructor. | Can be assigned a value only once, either at declaration or in a static constructor. |
Usage Context | Use when the value is known at compile time and will not change. | Use when the value may be set at runtime but should not change after initialization. | Use when the value is constant for all instances and should not change after initialization. |
Example | const int MaxValue = 100; | readonly int instanceValue; (assigned in constructor) | static readonly int StaticValue = 200; (assigned at declaration) |
Advance
Anonymous types
- Anonymous types are class types that derive directly from
System.Object
.
- They allow creating a new type without explicitly defining it first.
Key Features
- Created using the
new
operator with an object initializer syntax.
- Contain one or more public read-only properties. No other class members are allowed.
- The property initializer expressions cannot be
null
, anonymous functions, or pointer types.
- Inferred type based on the properties specified in the object initializer.
- Stored in implicitly typed variables declared using
var
.
- Scope is limited to the method where they are defined.
- Cannot be returned from a method or passed as a parameter (except as
object
).
- Two anonymous types are considered equal if all their properties are equal.
Example
var student = new { Id = 1, Name = "John Doe" }; Console.WriteLine(student.Name); // Output: John Doe
Garbage Collection in .NET
Garbage collection in C# is an automatic memory management process that helps reclaim memory occupied by objects that are no longer in use. This mechanism is a core feature of the .NET Framework, allowing developers to focus on application logic without worrying about manual memory management.
How Garbage Collection Works
The garbage collector (GC) operates in several phases:
- Marking Phase: The GC identifies live objects by traversing the object graph starting from root references. It creates a list of all objects that are still reachable.
- Relocating Phase: After marking, the GC updates references of the live objects to ensure they point to their new locations in memory, which may change during the compacting phase.
- Compacting Phase: The GC reclaims memory by removing dead objects (those not marked as live) and compacts the remaining live objects to reduce fragmentation. This involves moving the live objects towards the start of the heap, which optimizes memory usage and allocation speed.
Generations in Garbage Collection
C# employs a generation-based approach to optimize the garbage collection process, categorizing objects into three generations:
- Generation 0: This generation contains short-lived objects. Most objects are collected in this generation, making it the most frequently garbage-collected space.
- Generation 1: Objects that survive a collection in Generation 0 are promoted to Generation 1, which is collected less frequently.
- Generation 2: Long-lived objects that survive multiple collections are moved to Generation 2, which is collected even less often. This tier is generally stable and contains objects that are expected to remain in memory for a longer duration.
Benefits of Garbage Collection
- Automatic Memory Management: Developers do not need to manually allocate or free memory, reducing the likelihood of memory leaks and dangling pointers.
- Performance: While garbage collection can introduce pauses in application performance, it generally operates in the background with minimal impact on the application's responsiveness.
- Memory Safety: The GC ensures that objects cannot access memory allocated to other objects, enhancing the safety of the application
Managed and Unmanaged code
Managed Code
The code which is executed by CLR (Common Language Runtime) is called Managed Code, any application which is developed in .Net framework is going to work under CLR, the CLR internally uses the Garbage Collector to clear the unused memory and also used the other functionalities like CTS, CAS etc.

Unmanaged Code
The unmanaged code is basically developed using other languages (other than .Net Framework), so it uses its own language runtime to execute the applications. The application runtime will take care of its memory management, security etc...
Feature | Managed Code | Unmanaged Code |
Execution Environment | Executed by the Common Language Runtime (CLR) | Executed directly by the operating system |
Memory Management | Automatic memory management via garbage collection | Manual memory management using pointers |
Security | Benefits from CLR security features | Fewer security restrictions, higher risk of vulnerabilities |
Compilation | Compiled to Intermediate Language (IL) and JIT compiled to native code | Compiled directly to native machine code |
Performance | Slightly lower performance due to CLR overhead | Higher performance due to direct access to system resources |
Examples | .NET applications written in C#, VB.NET, F#, etc. | Native applications written in C/C++ |
What is a delegate in .NET?
Delegate is one of the base types in .NET. Delegate is a class, which is used to create and invoke delegates at runtime.
Key Characteristics of Delegates
- Type Safety: Delegates ensure that the method signature (return type and parameters) matches the delegate type, preventing runtime errors.
- Encapsulation: Delegates encapsulate a method, allowing methods to be passed as parameters, stored in variables, or invoked dynamically.
- Multicast Capability: Delegates can reference multiple methods at once, allowing for the invocation of several methods in a single call.
Built-in Delegates
C# provides several built-in delegate types that simplify common scenarios:
- Func<T, TResult>: Represents a method that takes parameters of type T and returns a value of type TResult. For example:
csharpFunc<int, int, int> multiply = (a, b) => a * b;
- Action<T>: Represents a method that takes parameters of type T and does not return a value. For example:
csharpAction<string> printMessage = message => Console.WriteLine(message);
- Predicate<T>: Represents a method that takes a parameter of type T and returns a boolean. For example:
csharpPredicate<int> isEven = number => number % 2 == 0;
Static
In C#, static classes and members provide a way to define functionality that does not require an instance of a class. This can be particularly useful for utility functions, constants, or shared data. Here’s a comprehensive overview of static classes, methods, and their characteristics.
Static Class
A static class is a class that cannot be instantiated, meaning you cannot create an object of that class. It is declared using the
static
keyword and can only contain static members (methods, properties, fields, etc.).Characteristics of Static Classes
- Cannot be instantiated: You cannot create an instance of a static class.
- Only contains static members: All members of a static class must be static. Attempting to define an instance member will result in a compile-time error.
- Sealed: Static classes are implicitly sealed, meaning they cannot be inherited.
- Memory Lifetime: A static class remains in memory for the lifetime of the application domain.
- Static Constructor: A static class can have a static constructor, which initializes static members when the class is loaded. Also, used as singleton.
Example of a Static Class
public static class MathUtility { public static double Pi = 3.14; public static double Square(double number) { return number * number; } } // Usage double area = MathUtility.Square(5); // Calls the static method Console.WriteLine("Area: " + area);
Static Members
Static members belong to the class itself rather than any instance of the class. They can be accessed without creating an instance of the class.
Types of Static Members
- Static Fields: Variables that are shared across all instances of a class. They retain their value for the lifetime of the application.
- Static Methods: Methods that can be called without creating an instance of the class. They cannot access instance members directly.
- Static Properties: Properties that are accessed through the class name rather than an instance.
- Static Events: Events that can be subscribed to without an instance of the class.
Example of Static Members in a Non-Static Class
public class Counter { public static int Count = 0; public Counter() { Count++; } } // Usage Counter c1 = new Counter(); Counter c2 = new Counter(); Console.WriteLine("Total Count: " + Counter.Count); // Outputs: Total Count: 2
Static Methods
Static methods can be defined in both static and non-static classes. They are called on the class itself rather than on an instance.
Characteristics of Static Methods
- Cannot access instance members: Static methods cannot directly access instance variables or instance methods.
- Can be overloaded: Static methods can have multiple definitions with different parameters.
- Cannot be overridden: Since they belong to the class, not to instances, static methods cannot be overridden in derived classes.
Example of Static Method
public class Utility { public static int Add(int a, int b) { return a + b; } } // Usage int sum = Utility.Add(5, 10); // Calls the static method Console.WriteLine("Sum: " + sum);
Static Constructors
A static constructor is used to initialize static members of a class. It is called automatically before any static member is accessed or any static method is called.
Characteristics of Static Constructors
- No parameters: Static constructors cannot take parameters.
- Called once: They are called only once, when the class is loaded.
- Cannot be called directly: You cannot call a static constructor directly.
Example of Static Constructor
public static class Configuration { public static string ConnectionString; static Configuration() { ConnectionString = "Server=myServer;Database=myDB;User Id=myUser;Password=myPass;"; } } // Usage Console.WriteLine(Configuration.ConnectionString); // Outputs the connection string
When to Use Static Classes and Members
- Utility Functions: For functions that do not rely on instance data, such as mathematical calculations (e.g.,
Math.Sqrt()
).
- Constants: To define constants that are shared across the application.
- Shared State: When you need to maintain a state that is shared across all instances of a class.
- Factory Methods: To create instances of classes without exposing the instantiation logic.
Lazy<T>
Lazy<T>
is a class in the .NET framework that provides support for lazy initialization, allowing you to defer the creation of an object until it is actually needed. This can improve performance, especially when the initialization of the object is resource-intensive or when it may not be needed during the application's lifetime.Key Features of Lazy<T>
- Deferred Initialization: The object of type
T
is created only when theValue
property is accessed for the first time, not when theLazy<T>
instance is created.
- Thread Safety:
Lazy<T>
provides thread-safe initialization by default. This means that if multiple threads try to access theValue
property simultaneously, the class ensures that the object is created only once.
- Custom Initialization Logic: You can provide a delegate (e.g., a lambda expression) to the
Lazy<T>
constructor, which defines how to create the instance ofT
.
- Control Over Thread Safety: You can specify the thread-safety mode using the
LazyThreadSafetyMode
enumeration, allowing you to choose between different levels of thread safety.
Constructors
- Lazy<T>(): Initializes a new instance of the
Lazy<T>
class using the default constructor of the target type.
- Lazy<T>(Func<T>): Initializes a new instance of the
Lazy<T>
class, using a delegate to specify how to create the target type.
- Lazy<T>(Func<T>, Boolean): Initializes a new instance of the
Lazy<T>
class, specifying whether the initialization is thread-safe.
- Lazy<T>(Func<T>, LazyThreadSafetyMode): Initializes a new instance of the
Lazy<T>
class, specifying the delegate and the thread-safety mode.
Example Usage
Here’s a simple example demonstrating how to use
Lazy<T>
:using System; public class ExpensiveObject { public ExpensiveObject() { // Simulate expensive initialization Console.WriteLine("ExpensiveObject created!"); } public void DoSomething() { Console.WriteLine("Doing something..."); } } public class Program { private static readonly Lazy<ExpensiveObject> lazyExpensiveObject = new Lazy<ExpensiveObject>(() => new ExpensiveObject()); public static void Main() { Console.WriteLine("Before accessing the Lazy object."); // The ExpensiveObject is not created yet Console.WriteLine("IsValueCreated: " + lazyExpensiveObject.IsValueCreated); // Accessing the Value property triggers the creation of ExpensiveObject var obj = lazyExpensiveObject.Value; obj.DoSomething(); // Now the object has been created Console.WriteLine("IsValueCreated: " + lazyExpensiveObject.IsValueCreated); } }
Output
Before accessing the Lazy object. IsValueCreated: False ExpensiveObject created! Doing something... IsValueCreated: True
When to Use Lazy<T>
- Resource-Intensive Objects: When the creation of an object is costly in terms of time or resources, and it may not be needed immediately.
- Conditional Initialization: When you want to delay the creation of an object until it is confirmed to be necessary.
- Singleton Implementations:
Lazy<T>
can simplify the implementation of thread-safe singletons, as it handles the lazy initialization and thread safety automatically.
Extension Methods
Extension methods in C# allow developers to add new methods to existing types without modifying their source code, creating a new derived type, or recompiling the original type. This feature enhances the functionality of types in a clean and maintainable way. Here’s an overview of extension methods based on the search results.
Key Features of Extension Methods
- Static Methods: Extension methods are defined as static methods in a static class.
this
Keyword: The first parameter of the method must be preceded by thethis
keyword, indicating which type is being extended.
- Instance Method Syntax: Once defined, extension methods can be called as if they were instance methods on the extended type.
- Namespace Scope: Extension methods are only available when the namespace containing them is imported with a
using
directive.
- Define a Static Class: Create a static class to contain the extension method.
- Define the Extension Method: Implement the method as a static method with the first parameter prefixed by
this
.
- Use the Extension Method: To use the extension method, import the namespace and call it like an instance method.
Example of Extension Method
Here’s a simple example demonstrating how to create and use an extension method:
using System; namespace StringExtensions { public static class StringHelper { // Extension method to reverse a string public static string Reverse(this string input) { char[] chars = input.ToCharArray(); Array.Reverse(chars); return new string(chars); } } } class Program { static void Main(string[] args) { string example = "Hello, World!"; string reversed = example.Reverse(); // Calling the extension method Console.WriteLine(reversed); // Output: !dlroW ,olleH } }
Ref Vs Out
Feature | ref | out | in |
Definition | Passes an argument by reference, allowing the method to modify the value of the argument. | Similar to ref , but used to return multiple values. | Passes an argument by reference, but the parameter is read-only within the method. |
Initialization Requirement | The variable must be initialized before being passed to the method. | The variable does not need to be initialized before being passed, but must be assigned a value within the method before return. | The variable must be initialized before being passed to the method. |
Modification | Allows the method to modify the value of the argument. | The method must assign a value to the out parameter before returning. | The method cannot modify the value of the argument; it is read-only. |
Use Case | When you want to modify the variable in the method. | When you want a method to return multiple values. | When you want to pass a large structure without copying it, but do not want to modify it. |
Example | void ModifyValue(ref int number) { number += 10; } | void GetValues(out int a, out int b) { a = 1; b = 2; } | void DisplayValue(in int number) { Console.WriteLine(number); } |
Parameter modifiers
Keyword | Description | Initialization Requirement | Use Case | Example |
ref | Passes an argument by reference, allowing the method to modify the value of the argument. | The variable must be initialized before being passed. | When you want to modify the variable in the method. | void ModifyValue(ref int number) {
number += 10;
} |
out | Similar to ref , but used to return multiple values. The variable does not need to be initialized. | Must be assigned a value within the method before return. | When you want a method to return multiple values. | void GetValues(out int a, out int b) {
a = 1; b = 2;
} |
in | Passes an argument by reference, but the parameter is read-only within the method. | The variable must be initialized before being passed. | When you want to pass a large structure without copying it, but do not want to modify it. | void DisplayValue(in int number) { Console.WriteLine(number);
} |
params | Allows a method to accept a variable number of arguments as an array. | The parameter does not need to be initialized explicitly. | When you want to pass a variable number of arguments to a method. | void PrintNumbers(params int[] numbers){
foreach (var number in numbers)
{ Console.WriteLine(number); }} |
Thread vs Task
Feature | Task | Thread |
Abstraction Level | Higher-level abstraction for asynchronous programming | Lower-level construct representing OS threads |
Memory Management | More lightweight and efficient, managed by TPL | Uses its own system resources like memory and CPU |
Execution Control | Simplifies concurrency management | Provides fine-grained control and allows setting priority |
Blocking the Main Thread | Does not block the main application thread from exiting | Blocks the main thread from exiting until it completes |
Ease of Use | Easier to use and compose, supports cancellation | More complex to manage and lacks built-in cancellation support |
Use Cases | Recommended for short-lived asynchronous operations | Better suited for long-running computations or unmanaged code |
QnA
What is the difference between string keyword and System.String class?
there is no difference. The string keyword is an alias for System.String class.
Are string objects mutable or immutable?
String objects are immutable. All of the String methods and C# operators that appear to modify a string actually return the results in a new string object.
What is a verbatim string literal and why do we use it?
The “@” symbol is the verbatim string literal. Use verbatim strings for convenience and better readability when the string text contains backslash characters, for example in file paths.
string ImagePath = @"C:\Images\Buttons\SaveButton.jpg"; //Output: C:\Images\Buttons\SaveButton.jpg string MultiLineText = @"This is multiline Text written to be in three lines."; /* Output: This is multiline Text written to be in three lines. */ string DoubleQuotesString = @"My Name is ""Pranaya."""; //Output: My Name is "Pranaya."
What is the difference between System.Text.StringBuilder and System.String?
This is one of the frequently asked C#.NET Interview Questions. Objects of type StringBuilder are mutable whereas objects of type System.String is immutable. As StringBuilder objects are mutable, they offer better performance than string objects of type System.String.
Difference between int and Int32 in C#
Int32 and int are synonymous, both of them allow us to create a 32-bit integer. int is shorthand notation (alias) for Int32.
What's the limit on stack to store value types
There are a few key limits related to storing value types on the stack in C#:
- Stack size: The default stack size for a 32-bit process in .NET is 1 MB, and for a 64-bit process it is 4 MB. This limits the total amount of memory that can be allocated on the stack.
- Maximum stack allocation size: While the stack size is 1 MB or 4 MB, the maximum size of a single stack allocation is lower. The exact limit depends on the specific version of .NET, but is typically around 1-2 MB. Trying to allocate more than this will result in a StackOverflowException.
- Numeric type limits: There are maximum and minimum values defined for each numeric value type in C#, based on the number of bits they use:
sbyte
: -128 to 127byte
: 0 to 255short
: -32,768 to 32,767ushort
: 0 to 65,535int
: -2,147,483,648 to 2,147,483,647uint
: 0 to 4,294,967,295long
: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807ulong
: 0 to 18,446,744,073,709,551,615
- Maximum string length: The maximum length of a string in C# is
Int32.MaxValue
, which is 2,147,483,647 characters. This limit is imposed by the .NET runtime due to memory constraints.
What are Access Modifiers in C#?
- Public: The public type or member can be accessed by any other code in the same assembly or another assembly that references it.
- Private: The type or member can only be accessed by code in the same class or struct.
- Protected: The type or member can only be accessed by code in the same class or struct, or in a derived class.
- Internal: The type or member can be accessed by any code in the same assembly, but not from another assembly.
- Protected Internal: The type or member can be accessed by any code in the same assembly, or by any derived class in another assembly.
What is CLR
Common language runtime (CLR). The CLR manages the life cycle and executes .NET applications (code). It also provides services that make the development process easier.
The runtime provides the following benefits:
- Performance improvements.
- Language features such as inheritance, interfaces, and overloading for object-oriented programming.
- Garbage collection.
- Use of delegates instead of function pointers for increased type safety and security.
What is CTS?
Languages including Common Type System (CTS) and Common Language Specification (CLS) that define the specifications of how types are defined and works is another major component of .NET.

What is the difference between a Queue and a Stack in .NET
Queue and stack are two common implementations when creating linked lists. A queue uses the first-in-first-out algorithm. The stack uses a last-in-first-out algorithm. Both are generic collections in C# and .NET.
What is the purpose of Generics in .NET
Generics in C# and .NET procedure many of the benefits of strongly-typed collections as well as provide a higher quality of and a performance boost for code. .NET Generics are not limited to classes only. In fact they can also be implemented with Interfaces, Delegates and Methods.
class DataStore<T> { public T Data {get; set;} } DataStore<string> store = new DataStore<string>();
What is the difference between Deep Copy and Shallow Copy?
When implementing copy constructors, it's essential to understand the difference between shallow and deep copies:
- Shallow Copy: A shallow copy creates a new object, but the fields of the new object reference the same memory locations as the original object. Changes to reference-type fields in the new object will affect the original object.
- Deep Copy: A deep copy creates a new object and recursively copies all the fields of the original object, including any referenced objects. The new object is entirely independent of the original.
What's the use of copy constructor
A copy constructor in C# is a special type of constructor that initializes a new object as a copy of an existing object. It is particularly useful when you want to create a duplicate of an object while ensuring that the new object has the same state as the original. This concept is essential in scenarios where you need to manage mutable objects, allowing modifications to the new object without affecting the original.
Key Uses of Copy Constructors
- Object Duplication: Copy constructors allow you to create a new object that is a copy of an existing object. This is useful when you need to duplicate the state of an object for further processing without altering the original object.
- Avoiding Side Effects: When working with mutable objects, changes made to the copied object should not affect the original object. A copy constructor ensures that the new object is independent of the original.
- Encapsulation: By using a copy constructor, you can maintain proper encapsulation, avoiding direct manipulation of an object's internal state from outside the class.
- Passing Objects by Value: When passing objects to methods, using a copy constructor can help ensure that the method works with a copy rather than the original object, preventing unintended modifications.
What's a private constructor?
A private constructor in C# is a special instance constructor that is declared with the ‘private’ access modifier. It can only be called within the class itself and is often used for specific purposes, such as implementing design patterns like the Singleton pattern or providing controlled instance creation.Key points about private constructors:
- Prevents external instantiation: Since a private constructor can only be accessed within the class, it prevents the creation of instances of the class from outside the class.
- Used with static members: Private constructors are commonly used in classes that contain static members only. These classes are not meant to be instantiated.
- Singleton pattern implementation: Private constructors are essential for implementing the Singleton pattern, which ensures that a class has only one instance.
- Prevents automatic default constructor generation: If a class has only private constructors (with no parameters), the compiler will not generate a default constructor automatically.
- Nested classes can access private constructors: While other classes cannot create instances of a class with a private constructor, nested classes can access the private constructor.
Here's an example of a private constructor:
public class Singleton { private static Singleton instance = null; private Singleton() { *// Private constructor to prevent external instantiation* } public static Singleton GetInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }`
What’s the difference b/w a class with private constructor and static class?
Key Differences
Instantiation:
- A static class cannot be instantiated at all.
- A class with a private constructor cannot be instantiated from outside the class but can have instances created through controlled means (like a singleton).
Members:
- A static class can only contain static members.
- A class with a private constructor can contain both static and instance members.
Inheritance:
- Static classes cannot be inherited.
- A class with a private constructor can be inherited by nested classes.
Can “this” be used within a Static Method?
- No, “this” cannot be used within a static method.
- Because the keyword 'this' returns a reference to the current instance of the class containing it.
What is the use of the Yield keyword in C#?
The yield keyword in C# is used to simplify the creation of iterator blocks, allowing for custom iteration over collections. It enables methods to return an
IEnumerable or IEnumerator without the need to create a temporary collection to hold the results. Here’s a summary of its uses based on the search results:
Uses of the yield
Keyword
- Iterator Blocks: The
yield
keyword informs the compiler that the method in which it appears is an iterator block. This allows the method to return values one at a time, preserving the state between calls.
yield return
: This statement is used to return the next value in the iteration. Each time theyield return
statement is executed, the current location in the code is saved, and execution is paused until the next iteration is requested.- Example:
public static IEnumerable<int> GetNumbers() { for (int i = 0; i < 5; i++) { yield return i; // Returns the current value of i } }
yield break
: This statement is used to terminate the iteration. It signals that there are no more values to return, effectively ending the iteration early.- Example:
public static IEnumerable<int> GetPositiveNumbers(IEnumerable<int> numbers) { foreach (int number in numbers) { if (number < 0) yield break; // Stops iteration if a negative number is encountered yield return number; // Returns positive numbers } }
- Lazy Evaluation: The
yield
keyword allows for lazy evaluation of collections. This means that values are generated on-the-fly as they are requested, rather than being computed and stored all at once. This can lead to improved performance and reduced memory usage.
- Stateful Iteration: With
yield
, you can maintain state across iterations without needing to manage the state explicitly. This makes it easier to create complex iteration logic.
What is a base keyword?
- The base keyword is used to access members of the base class from within a derived class.
- Call a method on the base class that has been overridden by another method.
- Specify which base-class constructor should be called when creating instances of the derived class.
- It is an error to use the base keyword from within a static method.