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.