48. protected
A protected member is accessible within the same class
and derived classes but not from outside classes.
Example:
class Parent {
protected int data
= 42;
}
class Child : Parent {
public void
ShowData() {
Console.WriteLine(data); //
Allowed
}
}
This is useful for inheritance, allowing derived
classes to use certain members without exposing them globally.
49. internal
An internal member is accessible only within the same
assembly (project) but not from another project that references it.
Example:
internal class InternalClass {
public void
Method() { }
}
Useful for defining APIs within an application while
preventing external access.
50. protected internal
A protected internal member is accessible within the same
assembly OR from derived classes (even in different assemblies).
Example:
protected internal int value;
This provides a mix of protected and internal access,
useful in libraries where subclasses need access but not unrelated classes.
51. private protected
A private protected member is accessible only within the
same assembly AND from derived classes. This was introduced in C# 7.2
for tighter access control.
Example:
class Parent {
private protected
int data = 10;
}
Only derived classes inside the same assembly can
access it.
52. sealed override
A sealed override prevents further overriding of a virtual
method.
Example:
class Base {
public virtual
void Show() { }
}
class Derived : Base {
public sealed
override void Show() { }
}
Any class that extends Derived cannot override Show(). This
is useful when you want to override a base method once but prevent further
changes.
53. static constructor
A static constructor initializes static members
of a class and executes only once when the class is first used. It has no
parameters and cannot be called manually.
Example:
class Example {
static Example() {
Console.WriteLine(“Static constructor executed”);
}
}
Used to initialize static fields or perform
one-time setup before any instance is created.
54. constant
A constant in C# is a value that is declared at
compile-time and remains unchanged throughout the program’s execution. It
is defined using the const keyword and must be assigned at the time of
declaration. Once set, it cannot be modified later, making it ideal for
fixed values like mathematical constants, configuration settings, or predefined
strings.
Example:
class Example {
public const
double Pi = 3.14159;
}
Here, Pi is a constant and cannot be changed anywhere in the
program.
Key Features:
- Stored
in memory at compile time. - Faster
than variables because the compiler replaces them with their actual
value. - Cannot
hold objects or reference types, except for string. - Scope
can be public, private, protected, etc.
Limitations:
- Cannot
be assigned at runtime (e.g., values from user input or configuration
files). - Cannot
hold instance-specific data—only static or global values. - Any
change requires recompilation of the entire program, which can be
problematic for configurable settings.
Constants are useful for fixed values but should be used
sparingly when flexibility is required.
55. readonly
A readonly field is a special type of variable that can
be assigned only once after declaration, either at the time of declaration
or inside a constructor. Unlike const, it can be initialized at
runtime, making it useful for values that must be set once but can depend
on input, configuration, or instance-specific data.
Example:
class Example {
public readonly
int InstanceID;
public Example(int
id) {
InstanceID =
id; // Allowed inside constructor
}
}
Here, InstanceID can be assigned a value when an object is
created, but after that, it cannot be modified.
Key Features:
- Can
store instance-specific values. - Works
with both value types and reference types (unlike const). - Allows
initialization at declaration or within a constructor.
Differences from const:
- readonly
fields can be initialized at runtime; const must be assigned at
compile-time. - readonly
supports objects and reference types, whereas const does not.
Use readonly when a value should not change after
initialization but depends on external factors (e.g., configuration
settings or runtime calculations).
56. var keyword
The var keyword enables type inference, meaning the
compiler automatically determines the type of a variable based on the assigned
value. It makes code more concise and readable without sacrificing type
safety.
Example:
var number = 10;
// Compiler infers int
var name = “Szabi”; // Compiler infers string
var list = new List<int>(); // Compiler infers
List<int>
Despite using var, the compiler still enforces strong
typing—once a type is inferred, it cannot be changed later.
Advantages:
- Less
typing, better readability, especially when working with complex types
like dictionaries or generics. - Ensures
type safety, unlike dynamic. - Helps
in anonymous types, where explicit declaration is impossible.
Limitations:
- Cannot
be used for class fields or method parameters (only for local
variables). - Reduces
explicit type clarity—can be harder to read when overused.
Use var for concise, readable code but avoid
excessive use when clarity is needed.
57. dynamic keyword
The dynamic keyword allows runtime type resolution,
meaning the compiler defers type checking until the program runs. Unlike var,
which infers a fixed type at compile time, dynamic variables can change
types at runtime.
Example:
dynamic value = 10;
// Initially an int
value = “Hello”; // Now a string
This flexibility makes dynamic useful in scenarios like reflection,
COM interactions, or JSON deserialization.
Key Features:
- No
compile-time type checking—errors appear at runtime. - Supports
dynamic method calls, meaning you can invoke methods that may not
exist at compile time. - Used
extensively in interoperability scenarios (e.g., Office COM
objects, scripting engines).
Risks:
- No
IntelliSense support in IDEs because the compiler does not know the
type. - Errors
are discovered at runtime, making debugging harder.
Use dynamic only when necessary, especially in
external API integrations or when working with unknown types.
58. in keyword
The in keyword is used to pass parameters by reference
while ensuring they cannot be modified within the method. It allows
efficient passing of large structures without making a copy, improving
performance in performance-critical applications.
Example:
void Print(in int number) {
Console.WriteLine(number);
}
int value = 10;
Print(value); //
Allowed
// value = 20; // Error! Cannot modify inside the method
Key Features:
- Improves
performance by avoiding unnecessary copies of large objects. - Ensures
read-only access within the method. - Works
well with structs and value types to optimize memory usage.
Best used in scenarios where data should not be modified
but needs efficient passing.
59. out keyword
The out keyword is used to pass arguments by reference,
ensuring the called method assigns a value before returning. It is
commonly used for multiple return values.
Example:
void GetValues(out int x, out int y) {
x = 10;
y = 20;
}
int a, b;
GetValues(out a, out b);
// a = 10, b = 20
Key Features:
- The
method must assign a value before returning. - Allows
returning multiple values from a function. - Often
used with TryParse methods (e.g., int.TryParse).
Use out when a method needs to return multiple values
without using tuples or objects.
60. ref keyword
The ref keyword passes a parameter by reference,
allowing both reading and writing of its value. Unlike out, it requires an
initial value before being passed.
Example:
void Modify(ref int x) {
x *= 2;
}
int num = 10;
Modify(ref num); //
num becomes 20
Key Features:
- Allows
modifying the original variable inside the method. - Must
be initialized before passing (unlike out). - Useful
in performance-sensitive applications to avoid copying large structures.
Use ref when a method needs to modify a variable while
keeping its original reference.