86. checked
The checked keyword in C# enforces overflow checking in arithmetic operations and conversions. By default, integer operations in C# do not throw exceptions when an overflow occurs; they wrap around instead, meaning values "roll over" when they exceed the type's capacity. The checked keyword prevents this behavior by explicitly throwing an OverflowException when an overflow happens.
Example:
checked
{
int max = int.MaxValue;
int result = max + 1; // Throws OverflowException
}
Without checked, this would wrap around and result would contain an unexpected negative number instead of throwing an error.
The checked block can be applied to multiple operations:
checked
{
int a = 2000000000;
int b = 2000000000;
int c = a + b; // Throws OverflowException
}
Alternatively, you can use the checked expression for individual operations:
int x = checked(int.MaxValue + 1); // Throws OverflowException
Use Cases:
✔ Ensuring correctness in financial,
scientific, or security-critical applications where overflow should never be
ignored.
✔ Preventing unintended overflows in
mathematical calculations.
✔ Debugging unexpected numerical
errors.
However, since checked introduces runtime overhead, it is typically disabled in performance-critical code unless absolutely necessary.
87. unchecked
The unchecked keyword disables overflow checking, allowing arithmetic operations to wrap around without throwing an exception. This is the default behavior for arithmetic operations in C#.
Example:
unchecked
{
int max = int.MaxValue;
int result = max + 1; // Wraps around to int.MinValue
}
Instead of throwing an exception, result will hold int.MinValue (-2147483648).
The unchecked keyword is also useful for individual expressions:
int x = unchecked(int.MaxValue + 1); // No exception, wraps around
✔ Use Cases:
- Optimizing performance by avoiding runtime overflow checks.
- Working with legacy code or situations where wraparound behavior is intentional.
- Handling special scenarios like cryptography or bitwise operations.
However, using unchecked carelessly can lead to silent data corruption, so it should be used with caution.
88. stackalloc
The stackalloc keyword in C# allocates memory on the stack instead of the heap, making it much faster than heap-based allocation. However, it can only be used within an unsafe context and only for fixed-size buffers of value types (like int, char, or byte).
Example:
unsafe
{
int* buffer = stackalloc int[5];
buffer[0] = 10;
Console.WriteLine(buffer[0]); // Output: 10
}
Key Features:
✔ High
performance – Stack memory is much faster than heap memory.
✔ Automatically freed –
Memory allocated with stackalloc is freed when the method exits (no need for
garbage collection).
✔ Safer alternative to heap
allocation – Useful for scenarios requiring low latency.
However, stackalloc has limitations:
⚠ Limited size – Since it
allocates on the stack, excessive allocation can cause stack overflow.
⚠ Only works in unsafe contexts
– Requires /unsafe compiler flag.
Use Cases:
✔ Performance-critical applications
(e.g., game engines, real-time systems).
✔ Temporary
buffers where garbage collection overhead should be minimized.
✔ Scenarios requiring direct
memory access, like interop with unmanaged code.
89. fixed statement
The fixed statement in C# "pins" an object in memory, preventing the garbage collector from moving it. This is essential when working with pointers in an unsafe context or when interoperating with unmanaged code (such as C libraries).
Example:
unsafe
{
int number = 42;
fixed (int* ptr = &number)
{
Console.WriteLine(*ptr); // Output: 42
}
}
Without fixed, the garbage collector might relocate number, making ptr invalid.
✔ Common Use Cases:
- Interop with unmanaged code (e.g., P/Invoke, COM).
- Working with fixed-size buffers in structs.
- Optimizing performance when frequent memory allocations could cause GC overhead.
However, excessive use of fixed can lead to performance degradation, as pinned objects hinder the efficiency of the garbage collector. It should only be used when absolutely necessary.
90. preprocessor directives
Preprocessor directives in C# control conditional compilation at compile time. They do not affect runtime behavior but allow developers to include or exclude code based on compilation settings.
Example:
#define DEBUG_MODE
using System;
class Program
{
static void Main()
{
#if DEBUG_MODE
Console.WriteLine("Debugging enabled.");
#else
Console.WriteLine("Release mode.");
#endif
}
}
If DEBUG_MODE is defined, "Debugging enabled." will be printed; otherwise, "Release mode." is printed.
✔ Common Preprocessor Directives:
- #define SYMBOL → Defines a compilation symbol.
- #if SYMBOL → Includes code only if SYMBOL is defined.
- #else → Provides an alternative code block.
- #elif → Checks another condition.
- #endif → Ends a preprocessor block.
- #error → Forces a compilation error (useful for enforcing conditions).
✔ Use Cases:
- Enabling debugging/logging in development but disabling it in production.
- Writing cross-platform code where certain features are only available in specific environments.
- Excluding experimental features from release builds.
Since preprocessor directives affect code readability, they should be used sparingly. For runtime conditions, standard if statements are preferable.
91. #region
The #region directive is a code organization tool that allows developers to collapse and expand sections of code in an IDE (such as Visual Studio). It does not affect the compiled program but improves readability in large code files.
Example:
#region Public Methods
public void Start()
{
Console.WriteLine("Starting...");
}
public void Stop()
{
Console.WriteLine("Stopping...");
}
#endregion
In Visual Studio, the #region section can be collapsed to hide its contents, making navigation easier.
✔ Use Cases:
- Grouping related methods, properties, or fields together.
- Improving readability in large classes.
- Temporarily hiding irrelevant sections while editing.
However, overusing #region can make code harder to navigate if misused. A well-structured codebase should not require excessive use of regions; proper class and method design should naturally provide clarity.
92. partial struct
A partial struct in C# allows a structure (struct) definition to be split across multiple files. This is useful when working with large or auto-generated codebases, as it helps improve code organization, readability, and maintainability.
How It Works
A struct can be defined in multiple parts, provided that each part uses the partial keyword:
Example: Splitting a Struct Across Files
File 1: Person1.cs
public partial struct Person
{
public string FirstName;
public string LastName;
}
File 2: Person2.cs
public partial struct Person
{
public int Age;
public void DisplayInfo()
{
Console.WriteLine($"{FirstName} {LastName}, Age: {Age}");
}
}
Now, the compiler combines these parts into a single struct at compile time, allowing Person to be used as if it were written in a single file:
Person p = new Person { FirstName = "John", LastName = "Doe", Age = 30 };
p.DisplayInfo(); // Output: John Doe, Age: 30
✔ Use Cases:
- Organizing large struct definitions by separating concerns.
- Auto-generated code (e.g., structs generated by tools like Entity Framework or protobuf).
- Enhancing collaborative development, where different teams manage different aspects of a struct.
⚠ Limitations:
- All parts must be in the same namespace and assembly.
- Cannot split a struct between different projects.
- Partial structs cannot have different accessibility modifiers in different files.
93. partial interface
A partial interface allows an interface definition to be split across multiple files, just like partial classes or structs. This feature is particularly useful for large or evolving API designs, enabling better separation of concerns and organization.
Example: Splitting an Interface
File 1: IEmployee1.cs
public partial interface IEmployee
{
string GetName();
}
File 2: IEmployee2.cs
public partial interface IEmployee
{
int GetAge();
}
Now, the compiler merges the interface into a single definition:
public class Employee : IEmployee
{
public string GetName() => "John Doe";
public int GetAge() => 30;
}
✔ Use Cases:
- Defining large interfaces with multiple related responsibilities across multiple files.
- Splitting auto-generated and manually written code (e.g., APIs generated by tools like Swagger).
- Supporting modular development, where different teams work on different aspects of an interface.
⚠ Limitations:
- Partial interfaces must be in the same namespace and assembly.
- Method signatures cannot be duplicated across files.
Since interfaces define contracts, partial interfaces are not as commonly used as partial classes or structs, but they can be useful in large projects where API evolution requires modular interface definitions.