C# Basics


C# Basics Interview with follow-up questions

1. Can you explain the difference between value types and reference types in C#?

In C#, every type is either a value type or a reference type — this distinction affects memory layout, copy semantics, and performance.

Value types store their data directly:

  • Stored inline — on the stack (for local variables) or embedded within a containing object/struct on the heap
  • Assignment copies the entire value
  • Examples: int, double, bool, char, decimal, DateTime, Guid, enum, struct
  • Cannot be null by default (use int? / Nullable for nullable value types)

Reference types store a reference (pointer) to heap-allocated data:

  • Variable holds a reference; assignment copies only the reference, not the object
  • Multiple variables can point to the same object (aliasing)
  • Examples: class, interface, delegate, object, string, arrays, record (by default)
  • Default value is null

Code illustration:

int a = 5;
int b = a;   // b is an independent copy
b = 10;
Console.WriteLine(a); // 5 — unchanged

var p1 = new Point { X = 1 };
var p2 = p1;  // if Point is a class, p2 references the same object
p2.X = 99;
Console.WriteLine(p1.X); // 99 if class, 1 if struct

Key gotchas interviewers test:

  • String: a reference type that behaves value-like due to immutability and interning. string equality (==) compares content, not reference.
  • Boxing: assigning a value type to object or an interface causes boxing — wraps it in a heap-allocated object. This has performance cost. Generics (List) avoid boxing.
  • readonly struct (C# 7.2+): prevents mutation, allows compiler optimizations (no defensive copies).
  • ref struct (C# 7.2+): stack-only structs like Span — cannot be boxed, cannot escape to the heap.
  • C# 8 nullable reference types: opt-in via enable in .csproj; the compiler warns about potential null dereferences on reference types. This doesn't change runtime behavior but catches bugs at compile time.
↑ Back to top

Follow-up 1

What happens when a value type is passed to a function?

When a value type is passed to a function in C#, a copy of the value is made. This means that any changes made to the value inside the function will not affect the original value outside the function. This is because value types are stored directly in memory and accessed by their value, so each copy of the value is independent of the others.

Follow-up 2

How does garbage collection work with reference types?

Garbage collection is a process in C# that automatically frees up memory that is no longer being used by reference types. When a reference type is created, memory is allocated to store the object. When the object is no longer needed, the garbage collector identifies it as eligible for collection and frees up the memory.

The garbage collector works by periodically scanning the memory to identify objects that are no longer reachable. It uses a technique called mark and sweep, where it marks all the objects that are still reachable and then sweeps through the memory to free up the memory occupied by the objects that are not marked.

It's important to note that the garbage collector is non-deterministic, meaning that you don't have control over when it runs. However, you can manually trigger garbage collection using the GC.Collect() method, although it's generally not recommended to do so.

Follow-up 3

Can you give an example of boxing and unboxing in C#?

Boxing and unboxing are operations in C# that allow you to convert a value type to a reference type and vice versa.

Boxing is the process of converting a value type to a reference type. This is done by wrapping the value type in an object. Here's an example:

int i = 42;
object obj = i; // Boxing

Unboxing is the process of converting a reference type back to a value type. This is done by extracting the value from the object. Here's an example:

object obj = 42;
int i = (int)obj; // Unboxing

It's important to note that boxing and unboxing can have performance implications, as they involve creating and extracting objects. Therefore, it's generally recommended to avoid unnecessary boxing and unboxing operations if possible.

2. What are the different types of loops in C# and how do they differ?

C# provides four loop constructs, each suited to different scenarios:

1. for loop — when the iteration count is known upfront:

for (int i = 0; i < 10; i++)
{
    Console.WriteLine(i);
}

2. while loop — when the condition is checked before each iteration:

int i = 0;
while (i < 10)
{
    Console.WriteLine(i++);
}

3. do-while loop — executes at least once; condition checked after:

int i = 0;
do
{
    Console.WriteLine(i++);
} while (i < 10);

4. foreach loop — iterates over any sequence implementing GetEnumerator():

foreach (var item in collection)
{
    Console.WriteLine(item);
}

foreach uses duck typing — any type with a GetEnumerator() method works, not just IEnumerable. This includes Span, ImmutableArray, and custom types.

Modern additions:

  • await foreach (C# 8+) — async streams:

    await foreach (var item in GetItemsAsync())
    {
    Console.WriteLine(item);
    }
    

    Requires the source to implement IAsyncEnumerable.

  • Parallel.For / Parallel.ForEach — data parallelism for CPU-bound work:

    Parallel.ForEach(items, item => Process(item));
    

Jump statements: break exits the loop, continue skips to the next iteration, return exits the method.

Interviewers probe: modifying a collection inside foreach throws InvalidOperationException — iterate over a snapshot (list.ToList()) if mutation is needed. Also: LINQ is often a cleaner functional alternative to explicit loops for transforming and filtering data.

↑ Back to top

Follow-up 1

Can you provide an example of a for loop?

Sure! Here's an example of a for loop in C#:

for (int i = 0; i < 5; i++)
{
    Console.WriteLine(i);
}

This for loop will iterate 5 times, starting from 0 and incrementing i by 1 in each iteration. It will print the values of i from 0 to 4.

Follow-up 2

When would you use a while loop instead of a for loop?

You would use a while loop instead of a for loop when you don't know the number of iterations in advance or when the number of iterations depends on a condition that may change during the loop execution. While loops are useful when you want to repeat a block of code until a certain condition is met.

Follow-up 3

What is the purpose of the 'break' statement in loops?

The 'break' statement is used to exit a loop prematurely. When the 'break' statement is encountered within a loop, the loop is immediately terminated, and the program execution continues with the next statement after the loop. 'break' is often used in combination with conditional statements to exit a loop based on a certain condition.

3. How do you declare and initialize an array in C#?

Basic array declaration and initialization:

// Fixed size, elements zero-initialized
int[] numbers = new int[5];

// Inline initialization — compiler infers size
int[] primes = new int[] { 2, 3, 5, 7, 11 };

// Shorthand (works at declaration)
int[] primes = { 2, 3, 5, 7, 11 };

// Collection expressions (C# 12+) — works with arrays, List, Span, etc.
int[] primes = [2, 3, 5, 7, 11];

Multi-dimensional arrays:

// Rectangular (true 2D block)
int[,] matrix = new int[3, 4];
int[,] grid = { {1,2}, {3,4}, {5,6} };

// Jagged (array of arrays — rows can have different lengths)
int[][] jagged = new int[3][];
jagged[0] = new int[] { 1, 2, 3 };
jagged[1] = new int[] { 4 };

Modern patterns:

  • Array.Empty() — returns a shared empty array instance; avoids allocating a new empty array.
  • Span and Memory — high-performance slice views over arrays (no allocation): csharp int[] data = [1, 2, 3, 4, 5]; Span slice = data.AsSpan(1, 3); // [2, 3, 4]
  • ArrayPool (System.Buffers) — rent and return large arrays to avoid GC pressure in hot paths.
  • Range and index operators (C# 8+): csharp int last = data[^1]; // last element int[] middle = data[1..4]; // elements at index 1, 2, 3

Prefer List over arrays when size is dynamic. Arrays are fixed-size; List grows automatically and is typically the better default for collections in application code.

Interviewers probe: what's the difference between a rectangular and a jagged array? (Rectangular is a single contiguous block; jagged is an array of array references, more flexible but requires separate allocations per row.)

↑ Back to top

Follow-up 1

How can you determine the length of an array?

In C#, you can determine the length of an array using the Length property. The Length property returns the total number of elements in the array.

For example, if you have an integer array numbers, you can determine its length using numbers.Length.

Here's an example:

int[] numbers = {1, 2, 3, 4, 5};
int length = numbers.Length; // length will be 5

Follow-up 2

What happens if you try to access an array element outside its bounds?

If you try to access an array element outside its bounds, i.e., an index that is less than 0 or greater than or equal to the length of the array, it will result in an IndexOutOfRangeException being thrown at runtime.

For example, if you have an array numbers with length 5, and you try to access numbers[5], it will throw an IndexOutOfRangeException.

To avoid this, you should always ensure that the index is within the valid range of the array length before accessing the element.

Follow-up 3

What is a multidimensional array?

A multidimensional array is an array that contains multiple dimensions or levels. In C#, you can create multidimensional arrays with two or more dimensions.

For example, a two-dimensional array is like a table with rows and columns. It can be declared and initialized using the following syntax:

[,]  = new [, ];

For example, to declare and initialize a two-dimensional integer array with 3 rows and 4 columns, you can use:

int[,] matrix = new int[3, 4];

You can access and modify the elements of a multidimensional array using multiple indices. For example, to access the element at row 1, column 2 of the matrix array, you can use matrix[1, 2].

4. What are the different types of operators in C#?

C# provides a rich set of operators organized by category:

Arithmetic: +, -, *, /, % (modulo). Integer division truncates toward zero. % on negative numbers keeps the sign of the dividend.

Assignment: =, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, >>>= (unsigned right shift, C# 11+).

Comparison / relational: ==, !=, <, >, <=, >=. For reference types, == compares references by default (unless overloaded — string overloads it for content equality).

Logical: && (short-circuit AND), || (short-circuit OR), ! (NOT). Non-short-circuit versions: &, | (also bitwise).

Bitwise: &, |, ^ (XOR), ~ (complement), << (left shift), >> (right shift), >>> (unsigned right shift, C# 11+).

Modern operators:

Operator Example Meaning
Null-conditional user?.Name Null if user is null, else user.Name
Null-conditional index list?[0] Null if list is null
Null-coalescing x ?? "default" Right side if left is null
Null-coalescing assignment x ??= new List() Assign if null
Range arr[1..4] Slice from index 1 to 3
Index from end arr[^1] Last element
Pattern is obj is string s Type check + cast
Switch expression x switch { 0 => "zero", _ => "other" } Expression form

Operator overloading: user-defined types can overload most operators with public static MyType operator+(MyType a, MyType b). Records support == / != overloading via synthesized value equality.

Interviewers probe: == vs .Equals()== for string compares content (overloaded), but for custom classes compares references unless overloaded. Always override both Equals and GetHashCode together.

↑ Back to top

Follow-up 1

Can you explain the difference between the '==' and '===' operators?

In C#, the '' operator is used for equality comparison, while the '=' operator is not a valid operator. The '' operator checks if the values of two operands are equal. For example, '5 == 5' would return true. On the other hand, the '=' operator is not a valid operator in C#. It is commonly used in other programming languages like JavaScript.

Follow-up 2

What is the use of the ternary operator?

The ternary operator in C# is a shorthand way to write an if-else statement. It allows you to assign a value to a variable based on a condition. The syntax of the ternary operator is 'condition ? value1 : value2'. If the condition is true, the value of the expression is 'value1', otherwise it is 'value2'. Here's an example:

int x = 5;
int y = (x > 0) ? 10 : -10;
// y will be assigned the value 10

Follow-up 3

How does the 'is' operator work in C#?

The 'is' operator in C# is used to check if an object is of a certain type. It returns true if the object is an instance of the specified type or a derived type, and false otherwise. The syntax of the 'is' operator is 'expression is type'. Here's an example:

object obj = new MyClass();
if (obj is MyClass)
{
    // obj is an instance of MyClass or a derived type
}

5. What are control statements in C# and can you provide some examples?

Control statements in C# direct the flow of execution based on conditions, loops, and jumps.

Conditional statements:

// if / else if / else
if (score >= 90)
    Console.WriteLine("A");
else if (score >= 80)
    Console.WriteLine("B");
else
    Console.WriteLine("C");

Switch statement (traditional):

switch (day)
{
    case DayOfWeek.Monday:
        Console.WriteLine("Monday");
        break;
    case DayOfWeek.Saturday:
    case DayOfWeek.Sunday:
        Console.WriteLine("Weekend");
        break;
    default:
        Console.WriteLine("Weekday");
        break;
}

Switch expression (C# 8+ — preferred for returning values):

string label = day switch
{
    DayOfWeek.Saturday or DayOfWeek.Sunday => "Weekend",
    DayOfWeek.Monday => "Start of week",
    _ => "Weekday"
};

Pattern matching in switch (C# 8+):

string Describe(object obj) => obj switch
{
    int n when n > 0    => $"Positive int: {n}",
    string s            => $"String of length {s.Length}",
    null                => "null",
    _                   => "Unknown"
};

Property patterns (C# 8+): case Person { Age: > 18 }. Positional patterns (C# 8+): case Point(0, 0). List patterns (C# 11+): case [1, 2, ..].

Jump statements: break (exit loop/switch), continue (next iteration), return, throw, goto (rare — useful for breaking nested loops).

Interviewers probe: C# switch does not fall through between cases by default (unlike C/Java) — each case must end with break, return, throw, or goto case. The switch expression (C# 8+) is the modern idiomatic choice when selecting a value.

↑ Back to top

Follow-up 1

What is the difference between 'if' and 'switch' statements?

The 'if' statement and the 'switch' statement are both control statements in C# that are used to make decisions and perform different actions based on certain conditions. However, there are some differences between them:

  1. Syntax: The syntax of the 'if' statement is more flexible and allows for complex conditions and multiple conditions to be evaluated. The syntax of the 'switch' statement is more concise and is used when you have a single variable or expression that you want to compare against multiple values.

  2. Evaluation: The 'if' statement evaluates a boolean expression and executes the block of code if the expression is true. The 'switch' statement evaluates the value of a variable or expression and compares it to the values specified in the 'case' labels. It executes the block of code associated with the first matching 'case' label.

  3. Fall-through: In the 'if' statement, once a condition is true and the corresponding block of code is executed, the program moves on to the next statement after the 'if' statement. In the 'switch' statement, if a 'case' label is matched and its block of code is executed, the program continues to execute the code in the subsequent 'case' labels unless a 'break' statement is encountered.

  4. Usability: The 'if' statement is more flexible and can handle complex conditions and multiple conditions. It is often used when the conditions are not easily expressed as a single variable or expression. The 'switch' statement is more concise and is often used when you have a single variable or expression that you want to compare against multiple values.

Overall, the choice between the 'if' statement and the 'switch' statement depends on the specific requirements of your program and the complexity of the conditions you need to evaluate.

Follow-up 2

How does the 'continue' statement work?

The 'continue' statement is a control statement in C# that is used to skip the rest of the current iteration of a loop and move on to the next iteration. It is often used in loops to skip certain iterations based on a condition. Here's how it works:

  1. For loop example:
for (int i = 0; i < 5; i++)
{
    if (i == 2)
    {
        continue;
    }
    Console.WriteLine(i);
}

Output:

0
1
3
4

In this example, the 'continue' statement is used to skip the iteration when 'i' is equal to 2. As a result, the number 2 is not printed.

  1. While loop example:
int i = 0;
while (i < 5)
{
    if (i == 2)
    {
        i++;
        continue;
    }
    Console.WriteLine(i);
    i++;
}

Output:

0
1
3
4

In this example, the 'continue' statement is used to skip the iteration when 'i' is equal to 2. The 'continue' statement is followed by the increment statement 'i++' to prevent an infinite loop.

The 'continue' statement can be used with any loop statement in C#, including 'for', 'while', 'do-while', and 'foreach'. It is a useful tool for controlling the flow of execution within a loop.

Follow-up 3

Can you provide an example of a nested if statement?

A nested if statement is an 'if' statement that is contained within another 'if' statement. It allows for more complex conditions and multiple levels of decision-making. Here's an example:

int x = 10;
int y = 5;

if (x > 5)
{
    if (y > 2)
    {
        Console.WriteLine("x is greater than 5 and y is greater than 2");
    }
    else
    {
        Console.WriteLine("x is greater than 5 but y is not greater than 2");
    }
}
else
{
    Console.WriteLine("x is not greater than 5");
}

Output:

x is greater than 5 and y is greater than 2

In this example, the outer 'if' statement checks if 'x' is greater than 5. If it is, the inner 'if' statement checks if 'y' is greater than 2. If both conditions are true, the message "x is greater than 5 and y is greater than 2" is printed. If the outer 'if' statement is false, the message "x is not greater than 5" is printed.

Nested if statements can be used to handle more complex conditions and perform different actions based on multiple levels of decision-making.

Live mock interview

Mock interview: C# Basics

Intermediate ~5 min Your own free AI key

Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.