Answers are provided in collapsible sections below each question — just click to expand.
In a quiz (5-10 randomly selected questions), you can test yourself here: https://dotnetrends.net/quiz/
Have fun, and good luck! Any comments and suggestions are more than welcome.
.NET Trends X (formerly Twitter) | LinkedIn
class Program
{
static void Main()
{
Console.WriteLine(Test(null));
}
static string Test(object o) => "object";
static string Test(dynamic d) => "dynamic";
}
- A: dynamic
- B: object
- C: Type 'Program' already defines a member called 'Test' with the same parameter types
- D: Cannot convert null literal to non-nullable reference type
Answer
In C#, method overloading is based on compile-time types. While dynamic behaves differently at runtime, at compile-time, dynamic is just an alias for object. Since method overloading does not allow two methods with identical signatures (same method name and parameter types), the compiler does not allow this code to compile.
class Program
{
static void Main()
{
int[] numbers = [ 1, 2, 3 ];
ModifyArray(numbers);
Console.WriteLine(numbers == null ? "Array is null" : $"Array is not null, Length: {numbers.Length}");
}
static void ModifyArray(int[] arr)
{
arr = null;
}
}
- A: Array is not null, Length: 3
- B: Array is not null, Length: 0
- C: Array is null
- D: Not all code paths return a value
Answer
In C#, method parameters are passed by value by default. For reference types (like classes and arrays), the reference itself is passed by value - meaning the method receives a copy of the reference, not the original reference. However, both references point to the same object in memory, so changes to the object's properties within the method will be visible outside the method.
When you call ModifyArray(numbers); the value of the reference (a pointer to the array in memory) is copied into the method parameter arr. So now, inside the method: arr is a copy of the reference to the original array numbers. If you do arr = null;, you're only modifying the copy, not the original. The original numbers in Main() remains untouched.
class Program
{
static object _lock = new object();
static async Task Main()
{
DoWorkAsync();
}
static async Task DoWorkAsync()
{
lock (_lock)
{
Console.WriteLine("Entered lock");
await Task.Delay(1000);
Console.WriteLine("Leaving lock");
}
}
}
- A: Entered lock, Leaving lock
- B: A deadlock
- C: No output
- D: Cannot await in the body of a lock statement
Answer
When you await inside a lock, you risk not releasing the lock immediately. When the method resumes, it might do so on a different thread that didn’t acquire the lock originally, violating the Monitor’s rules. This is why the compiler doesn't even let you do it. It throws an error to protect you from unsafe behaivour.
Instead, you should use SemaphoreSlim.
class Program
{
static void Main()
{
List<Action> actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
Action a = () => Console.WriteLine($"Lambda captured: {i}");
actions.Add(a);
}
foreach (var action in actions)
{
action();
}
}
}
- A: Lambda captured: 0 Lambda captured: 1 Lambda captured: 2
- B: Lambda captured: 3 Lambda captured: 3 Lambda captured: 3
- C: Lambda captured: 2 Lambda captured: 2 Lambda captured: 2
- D: Compiler error
Answer
Each lambda is capturing the variable i, not its value. The variable i is declared outside the lambda, and the lambda holds a reference to it. When the foreach runs the lambdas, the loop has already finished — and i == 3. So, the lambdas don’t capture 0, 1, 2 — they all reference the same variable, and its final value is 3.
To fix it introduce a variable in the loop:
for (int i = 0; i < 3; i++)
{
int copy = i; // 👈 new variable per iteration
actions.Add(() => Console.WriteLine($"Lambda captured: {copy}"));
}
class Program
{
static void Main()
{
SomethingAsync();
Console.WriteLine("Hello World!");
}
static async void SomethingAsync()
{
await Task.Delay(1000);
throw new Exception("Exception");
}
}
- A: The application crashes
- B: Hello World!
- C: Compiler error
- D: Hello World! and the application crashes.
Answer
When you call an async void method, it behaves like fire-and-forget. Control immediately returns to the caller — in this case, back to Main() — without waiting for the await to finish. Meanwhile, the await Task.Delay(1000) delays execution for 1 second. After that delay, the exception should be thrown — but by that time, Main has already printed the message and the application has exited.
class Program
{
static async Task Main()
{
try
{
var task = ThrowingMethodAsync();
Console.WriteLine("Task started, waiting for it to complete...");
}
catch (Exception ex)
{
Console.WriteLine($"Caught: {ex.Message}");
}
}
static async Task ThrowingMethodAsync()
{
await Task.Delay(1000);
throw new InvalidOperationException("Something went wrong");
}
}
- A: Task started, waiting for it to complete... Something went wrong
- B: Something went wrong
- C: Task started, waiting for it to complete...
- D: The application crashes
Answer
Because the async Task method runs in the background (it is not awaited) the Main method finishes immediately after the Console.WriteLine. Since the exception is thrown after Main is already done, it has no place to be caught, and the try-catch block around the var task = ... line is useless.
class Program
{
static void Main()
{
int[] numbers = [ 1, 2, 3 ];
ModifyArray(numbers);
Console.WriteLine(string.Join(", ", numbers));
ResetArray(numbers);
Console.WriteLine(string.Join(", ", numbers));
}
static void ModifyArray(int[] arr)
{
arr[0] = 99;
}
static void ResetArray(int[] arr)
{
arr = [0,0,0];
}
}
- A:
1, 2, 3
1, 2, 3 - B:
1, 2, 3
0, 0, 0 - C:
99, 2, 3
0, 0, 0 - D:
99, 2, 3
99, 2, 3
Answer
In ResetArray, arr is a copy of the reference to numbers. When we assign arr = [ 0, 0, 0 ], we are changing the local variable arr to point to a new array. But this change is not visible to the caller. The original numbers array is unchanged. So numbers still points to the array with values: 99, 2, 3.
class Program
{
static void Main()
{
string a = "hello";
string b = "he" + "llo";
string c = string.Concat("he", "llo");
string d = new string("hello".ToCharArray());
Console.WriteLine(object.ReferenceEquals(a, b));
Console.WriteLine(object.ReferenceEquals(a, c));
Console.WriteLine(object.ReferenceEquals(a, d));
}
}
- A: True, False, False
- B: True, True, True
- C: False, False, False
- D: True, True, False
Answer
string a = "hello"; this is a string literal, so it's interned — stored in the intern pool. a points to that interned string.
string b = "he" + "llo";
Both parts are string literals. Same interned string as a.
string c = string.Concat("he", "llo");
This is evaluated at runtime. Even though the value is "hello", the result is not interned automatically.
So c refers to a new string instance with the same content.
string d = new string("hello".ToCharArray());
This explicitly creates a new string object using a char[].
It will never refer to the interned version unless you manually intern it using string.Intern().
class Program
{
static void Main()
{
string a = "hello";
string b = a;
string c = "hello";
string d = new string("hello".ToCharArray());
Console.WriteLine(object.ReferenceEquals(a, b));
Console.WriteLine(object.ReferenceEquals(a, c));
Console.WriteLine(a == d);
Console.WriteLine(a.Equals(d));
}
}
- A: True, False, True, True
- B: True, True, True, True
- C: True, True, True, False
- D: False, False, True, True
Answer
True - a and b point to the same reference
True - a and c are both interned "hello"
True - a and d have same content (== is overridden)
True - Equals() checks content equality
class Program
{
private static int a = b;
private static int b = 10;
static void Main()
{
Console.WriteLine(a);
}
}
- A: 10
- B: Use of unassigned local variable 'b'
- C: Runtime Error
- D: 0
Answer
Static fields in C# are initialized in the order they are declared in the class, not in the order of their references in the code. a is declared first, and it is initialized with the value of b. However, b is declared after a, and its default value is 0 (because it's an int and the default value for int is 0).
class Counter
{
public static int Count = 0;
public static void Increment()
{
for (int i = 0; i < 1_000_000; i++)
{
Count++;
}
}
}
class Program
{
static async Task Main()
{
var task1 = Task.Run(() => Counter.Increment());
var task2 = Task.Run(() => Counter.Increment());
await Task.WhenAll(task1, task2);
Console.WriteLine($"Final Count: {Counter.Count}");
}
}
- A: Final Count: 1000000
- B: Final Count: 2000000
- C: More than 2000000
- D: Less than 2000000
Answer
The Count++ operation is not thread-safe, as it involves a read-modify-write sequence that can be interrupted by other threads.
Since both tasks run concurrently, increments may overlap, leading to lost updates and a final count less than 2000000. This happens because the static field is shared across threads, and no synchronization (like Interlocked or lock) is used.
class Program
{
static async Task Main()
{
try
{
ThrowingTask();
Console.WriteLine("Main continues...");
await Task.Delay(2000);
}
catch (Exception ex)
{
Console.WriteLine($"Caught: {ex.Message}");
}
}
static async Task ThrowingTask()
{
await Task.Delay(500);
throw new InvalidOperationException("Boom!");
}
}
- A: Main continues.. Caught: Boom!
- B: Main continues..
- C: No output
- D: Main continues.. Application crashes
Answer
The exception in ThrowingTask is thrown on a background thread, but since the task is not awaited, the exception is never observed by the Main method’s try-catch. Unobserved task exceptions can crash the application or be silently ignored, depending on the runtime and .NET version. This is why fire-and-forget tasks should be avoided unless properly handled, e.g., with logging or exception capturing.
class Program
{
static void Main()
{
int[] numbers = [ 1, 2, 3, 4, 5 ];
Span<int> slice = numbers.AsSpan(1, 3);
for (int i = 0; i < slice.Length; i++)
{
slice[i] *= 2;
}
Console.WriteLine(string.Join(", ", numbers));
}
}
- A: 2, 4, 6, 4, 5
- B: 1, 4, 6, 8, 5
- C: 1, 2, 3, 4, 5
- D: Compile-time error: cannot slice an array with Span
Answer
Span provides a view over the original memory, not a copy. In this case, numbers.AsSpan(1, 3) creates a span covering elements at index 1 to 3 (values 2, 3, 4), and the loop doubles those values in-place. Because Span directly references the original array memory, any changes to it reflect in the original array, making it both efficient and safe for high-performance scenarios without additional allocations.
class Base
{
public virtual void Show() => Console.WriteLine("Base.Show");
}
class Derived : Base
{
public void Show() => Console.WriteLine("Derived.Show");
}
class Program
{
static void Main()
{
Base obj = new Derived();
obj.Show();
}
}
- A: Runtime error
- B: Derived.Show
- C: No Output
- D: Base.Show
Answer
Base declares a virtual method Show(). Derived hides (not overrides!) the Show() method by declaring public void Show() without using the override keyword. At runtime, you're calling obj.Show() where obj is of type Base — and since method hiding (via new) is resolved by the compile-time type, Base.Show() is called, not Derived.Show().
To get polymorphic behavior (Derived.Show()), the method in Derived must be marked with override. Otherwise, it hides the base method, and calls are resolved by the reference type — not the instance type.
class Base
{
public static void WhoAmI() => Console.WriteLine("Base.Static");
public virtual void Identify() => Console.WriteLine("Base.Instance");
}
class Derived : Base
{
public new static void WhoAmI() => Console.WriteLine("Derived.Static");
public override void Identify() => Console.WriteLine("Derived.Instance");
}
class Program
{
static void Main()
{
Derived d = new Derived();
Base b = d;
Derived.WhoAmI();
Base.WhoAmI();
d.Identify();
b.Identify();
}
}
- A: Derived.Static Base.Static Derived.Instance Derived.Instance
- B: Base.Static Base.Static Derived.Instance Derived.Instance
- C: Derived.Static Base.Static Base.Instance Derived.Instance
- D: Compiler error
Answer
Derived.WhoAmI() calls the static method defined in Derived, which hides the one in Base, so it prints "Derived.Static".
Base.WhoAmI() calls the static method from Base, because static methods are resolved at compile time based on the reference type, not the runtime object type.
Both d.Identify() and b.Identify() call the overridden Identify method from Derived, because instance virtual methods are resolved at runtime based on the actual object type (Derived), so both print "Derived.Instance".
class Base
{
static Base()
{
Console.WriteLine("Static Base");
}
public Base()
{
Console.WriteLine("Instance Base");
}
}
class Derived : Base
{
private readonly string msg = PrintMsg();
static Derived()
{
Console.WriteLine("Static Derived");
}
public Derived()
{
Console.WriteLine("Instance Derived");
}
private static string PrintMsg()
{
Console.WriteLine("Field Initialization");
return "Done";
}
}
class Program
{
static void Main()
{
Derived d = new Derived();
}
}
-
A: Static Derived
Static Base
Field Initialization
Instance Base
Instance Derived -
B: Static Derived
Field Initialization
Static Base
Instance Base
Instance Derived -
C: Static Base
Field Initialization
Static Derived
Instance Base
Instance Derived -
D: Field Initialization
Static Base
Static Derived
Instance Base
Instance Derived
Answer
Derived's static constructor runs first because it's the first type used in Main, even though it's a derived class — static constructors are triggered based on usage, not inheritance chain. The instance field msg in Derived is initialized before the base constructor runs, so "Field Initialization" is printed next. After that, Base's static constructor runs, followed by its instance constructor, and finally Derived's instance constructor. Base class instance constructors are always called before derived class constructors.
struct S { }
class Program
{
static void Main()
{
S s = new S();
object o1 = s;
object o2 = s;
Console.WriteLine(object.ReferenceEquals(o1, o2));
}
}
- A: True
- B: False
- C: Runtime exception
- D: Compiler error
Answer
S is a value type (a struct), and when you assign it to object, boxing occurs — meaning the value is copied into a new object on the heap. When you do object o1 = s; and then object o2 = s;, each boxing operation creates a new boxed copy of the value. So o1 and o2 refer to different objects, even though they hold the same value — and therefore, object.ReferenceEquals(o1, o2) returns false.
public class A { }
public static class Extensions
{
public static void Foo(this A a) => Console.WriteLine("Extension");
}
public class B : A
{
public void Foo() => Console.WriteLine("Instance");
}
class Program
{
static void Main()
{
A obj = new B();
obj.Foo();
}
}
- A: Compiler error
- B: Extension
- C: Instance
- D: Runtime exception
Answer
B defines an instance method Foo(), but obj is declared as type A, and A does not have a method called Foo(). Since A has no Foo() instance method, the compiler uses the extension method defined in Extensions. Even though obj is actually a B at runtime, method resolution for extension methods is static, based on the compile-time type (A), not the runtime type (B). So Extension is printed.
public interface IAnimal { }
public class Dog : IAnimal { }
public static class Extensions
{
public static void Speak<T>(this T animal) where T : IAnimal
{
Console.WriteLine("Woof!");
}
public static void Speak(this object obj)
{
Console.WriteLine("Generic object");
}
}
class Program
{
static void Main()
{
object dog = new Dog();
dog.Speak();
}
}
- A: Compiler error
- B: Woof
- C: Generic object
- D: Runtime exception
Answer
The output is Generic object because the variable dog is declared as object, not as Dog or IAnimal. Extension methods are resolved at compile time based on the static type of the variable, and object does not meet the generic constraint where T : IAnimal. Therefore, the compiler picks the non-generic Speak(this object obj) extension method.
class Program
{
static ref int Find(ref int x, ref int y)
{
if (x > y)
return ref x;
else
return ref y;
}
static void Main()
{
int a = 5, b = 10;
ref int max = ref Find(ref a, ref b);
max = 100;
Console.WriteLine($"{a}, {b}");
}
}
- A: 100, 10
- B: 5, 100
- C: 5, 10
- D: 100, 100
Answer
The method Find returns a reference to either x or y, depending on which is greater. Since b (10) is greater than a (5), Find returns a reference to b, and max becomes a reference to b. Setting max = 100 changes the value of b, so the final output is 5, 100.
class Program
{
static ref int Select(ref int a, out int b)
{
b = a;
return ref a;
}
static void Main()
{
int x = 1;
ref int y = ref Select(ref x, out int z);
y = 5;
Console.WriteLine($"{x} {z}");
}
}
- A: 5 5
- B: 1 1
- C: 5 1
- D: 1 5
Answer
When Select is called, it assigns b = a, so z gets the original value of x, which is 1.
Then Select returns a reference to x, and y is a ref to x, so changing y = 5 actually modifies x to 5.
Thus, the final output is 5 1, because x became 5, but z (copied before modification) remains 1.
class Program
{
static void Main()
{
var numbers = Enumerable.Range(1, 5);
var query = numbers.Where(x => x % 2 == 0).Select(x => x * x);
numbers = Enumerable.Range(10, 5);
foreach (var n in query)
Console.Write(n + " ");
}
}
- A: 4 16
- B: 100 144
- C: Nothing (empty output)
- D: Throws an exception
Answer
LINQ uses deferred execution, meaning the query is not evaluated when defined, but only when enumerated inside foreach.
However, numbers was reassigned to a new range (10–14), and the query still refers to the old enumerable (1–5) because Enumerable.Range(1,5) produces an immutable sequence.
Thus, the original even numbers 2 and 4 are selected, squared to 4 and 16, and the output is 4 16.
class Program
{
static void Main()
{
int counter = 0;
var query = Enumerable.Range(1, 3).Select(x => counter++);
foreach (var item in query.Reverse())
Console.Write(item);
}
}
- A: Throws an exception
- B: 210
- C: 012
- D: 222
Answer
The LINQ query uses deferred execution, so counter++ is not evaluated until the query is enumerated.
When query.Reverse() is executed, it forces evaluation of Select, incrementing counter as it maps 0, 1, 2 (in order), and then reverses the result to 2, 1, 0.
class Program
{
static void Main()
{
var actions = Enumerable.Range(1, 3)
.Select(i => new Action(() => Console.Write(i)))
.ToArray();
foreach (var action in actions)
action();
}
}
- A: 333
- B: 123
- C: 111
- D: 321
Answer
The lambda () => Console.Write(i) captures the loop variable i from the LINQ Select statement.
In older C# versions (pre-C# 5), all the lambdas would share the same captured i, resulting in 333.
In modern C#, LINQ with Select(i => ...) creates a new i per iteration (no closure issue here), so each Action correctly captures a distinct value.
Therefore, when each Action in the array is invoked, it prints 1, 2, then 3.
class Program
{
static async Task Main()
{
var t1 = Task.Delay(1000).ContinueWith(_ => "First");
var t2 = Task.Delay(500).ContinueWith(_ => "Second");
var t3 = Task.Delay(2000).ContinueWith(_ => "Third");
var firstDone = await Task.WhenAny(t1, t2, t3);
Console.WriteLine(await firstDone);
var all = await Task.WhenAll(t1, t2, t3);
Console.WriteLine(string.Join(",", all));
}
}
- A: Second
First,Second,Third - B: Second
Third,Second,First - C: Second
Second,First,Third - D: First
Third,Second,First
Answer
Task.WhenAny(t1, t2, t3) returns the first completed task (not necessarily in the order written). t2 has the shortest delay (500ms), so it finishes first. Therefore, firstDone becomes t2. await firstDone unwraps the result of t2, which is "Second".
Task.WhenAll(t1, t2, t3) waits for all tasks to finish and gathers their results in the original order they were defined (t1, t2, t3). So the second output is: "First,Second,Third".
var numbers = Get();
if (numbers.Any())
{
Console.WriteLine("Looping");
foreach (var n in numbers)
Console.WriteLine(n);
}
IEnumerable<int> Get()
{
Console.WriteLine("Generating");
yield return 1;
yield return 2;
}
- A: Generating
Looping
1
2 - B: Generating
Looping
Generating
1
2 - C: Looping
1
2 - D: Looping
Answer
The numbers variable holds an IEnumerable produced by Get(), which uses yield return, meaning it's lazily evaluated.
The first call to numbers.Any() triggers enumeration, causing "Generating" to print once as it starts iterating to check if any elements exist.
The second enumeration in the foreach loop restarts the iterator, so "Generating" is printed again before yielding the values 1 and 2.
public class Program
{
public static void Main()
{
List<int> numbers = new List<int>{1, 2, 3, 4, 5};
var result = numbers.Where(n => n % 2 == 0).Select(n => n * n).Aggregate((a, b) => a + b);
Console.WriteLine(result);
}
}
- A: 20
- B: 14
- C: 10
- D: 4
Answer
Where(n => n % 2 == 0) filters even numbers: {2, 4}
Select(n => n * n) squares them: {4, 16}
Aggregate((a, b) => a + b) adds them: 4 + 16 = 20
public class Publisher
{
public delegate void Notify();
public event Notify ProcessCompleted;
public void RaiseEvent()
{
ProcessCompleted?.Invoke();
}
}
public class ExternalInvoker
{
public void TryInvokeEvent(Publisher publisher)
{
publisher.ProcessCompleted();
}
}
public class Program
{
public static void Main()
{
Publisher pub = new Publisher();
pub.ProcessCompleted += () => Console.WriteLine("Process Completed!");
ExternalInvoker invoker = new ExternalInvoker();
invoker.TryInvokeEvent(pub);
}
}
- A: The event is successfully invoked.
- B: The code runs, but the event is never triggered.
- C: The code compiles but throws a runtime exception.
- D: The code fails to compile with an error about event access.
Answer
Even though ProcessCompleted is a public event, C# restricts event invocation to the class that declares it (or its derived classes). Attempting to invoke it from outside the declaring class (as in ExternalInvoker) results in a compile-time error.
Func<int> f1 = () => { Console.Write("1"); return 10; };
Func<int> f2 = () => { Console.Write("2"); return 20; };
var f = f1 + f2;
int result = f();
Console.Write($":{result}");
- A: 12:10
- B: 21:20
- C: 12:20
- D: 12:30
Answer
In C#, multicast delegates (created using +) invoke all combined methods, but only the result of the last delegate is returned. f1 prints "1" and returns 10. f2 prints "2" and returns 20.
When calling f(), both f1 and f2 are invoked, in order (f1, then f2), so Console.Write("1") then Console.Write("2") happens — output: 12. f() returns the result of f2, which is 20, and that is printed after the colon.
class A { }
class B : A { }
class Program
{
static void Print<T>(T item) where T : A => Console.WriteLine("Generic");
static void Print(A item) => Console.WriteLine("Non-generic");
static void Main()
{
B b = new B();
Print(b);
}
}
- A: Generic
- B: Non-generic
- C: Compiler error
- D: Runtime error
Answer
When the method Print(b) is called with an object of type B, the compiler considers both the generic method Print(T item) and the non-generic method Print(A item).
The method Print(T item) can be used because B is a subclass of A, and it matches the constraint where T : A. The compiler will infer T as B, making it a valid call for the generic method.
In C#, the generic method is preferred over the non-generic one when the generic method's type constraint is satisfied. In this case, B satisfies T : A, so the generic method is chosen.
interface IProducer<T>
{
T Produce();
}
class Animal { public override string ToString() => "Animal"; }
class Dog : Animal { public override string ToString() => "Dog"; }
class DogProducer : IProducer<Dog>
{
public Dog Produce() => new Dog();
}
class Program
{
static void Main()
{
IProducer<Dog> dogProducer = new DogProducer();
IProducer<Animal> animalProducer = dogProducer;
Console.WriteLine(animalProducer.Produce());
}
}
- A: Compilation error
- B: Runtime InvalidCastException
- C: Prints "Dog"
- D: Prints "Animal"
Answer
In C#, generic interfaces are not covariant by default unless explicitly declared with the out keyword on the type parameter. IProducer interface is declared as interface IProducer, without covariance. Therefore, even though Dog derives from Animal, IProducer cannot be assigned to IProducer.
So the line: IProducer<Animal> animalProducer = dogProducer;
causes a compile-time error: "Cannot implicitly convert type IProducer<Dog>
to IProducer<Animal>
."
To make this code compile the interface should be declared as: interface IProducer<out T>
class Program
{
static void Main()
{
foreach (var x in Generate())
{
Console.WriteLine(x);
break;
}
}
static IEnumerable<int> Generate()
{
Console.WriteLine("Start");
yield return 1;
Console.WriteLine("End");
}
}
- A: Compilation error
- B: Start 1 End
- C: Start 1
- D: Start End 1
Answer
When Generate() is called in the foreach, the method body doesn't execute immediately. It starts execution only when the enumerator is advanced (e.g., by entering the loop).
The line Console.WriteLine("Start") is printed right before the first yield return 1.
After yield return 1, control returns to the loop, which prints 1 and breaks. Because of the break, the iterator is not advanced further, so "End" is never printed.
class Box
{
public int Value;
}
class Program
{
static void Main()
{
var x = new Box { Value = 100 };
var y = x;
Modify(ref x);
Console.WriteLine(y.Value);
}
static void Modify(ref Box b)
{
b = new Box { Value = 200 };
}
}
- A: Runtime error
- B: 200
- C: No output
- D: 100
Answer
Initially, both x and y reference the same Box object on the heap (Value = 100).
When Modify(ref x) is called, it replaces the reference in x with a new Box instance (Value = 200). However, this change only affects x because it’s passed by reference.
y still points to the original Box instance (Value = 100), so Console.WriteLine(y.Value) prints 100.
class Program
{
static string Format(int x) => $"({x})";
static object Box(int x) => x;
static void Main()
{
var funcs = new[] { (Func<int, object>)Format, Box };
Console.WriteLine(funcs );
}
}
- A: Runtime error
- B: System.Func`2[System.Int32,System.Object][]
- C: System.Func`2[System.Int32,System.String][]
- D: System.Func cannot be inferred from method group
Answer
The line var funcs = new[] { (Func<int, object>)Format, Box };
causes both Format and Box to be treated as Func<int, object>
. Although Format returns string, and string is implicitly convertible to object, this cast makes the method group compatible with Func<int, object> through covariance in the return type. The compiler infers the array type as Func<int, object>[]. This compiles because both method references can be converted to that delegate type, even though their actual return types differ.
class A
{
static A()
{
Console.WriteLine("Static A");
}
public A()
{
Console.WriteLine("Instance A");
}
public static void SayHello() => Console.WriteLine("Hello from A");
}
class Program
{
static void Main()
{
A.SayHello();
var obj = new A();
}
}
- A: Static A, Hello from A, Instance A
- B: Hello from A, Instance A
- C: Static A, Instance A
- D: Hello from A
Answer
When the static method SayHello is first accessed, the runtime invokes the static constructor static A() before executing SayHello(). Then, creating a new instance with new A() triggers the instance constructor, printing "Instance A". Static constructors are called automatically once before any static members are accessed or an instance is created, whichever comes first.
record Person
{
public string Name { get; set; }
}
class Program
{
static void Main()
{
var p1 = new Person { Name = "Alice" };
p1.Name = "Bob";
Console.WriteLine($"{p1.Name}");
}
}
- A: Alice
- B: Bob
- C: Complication error
- D: Runtime error
Answer
The Person record has a mutable Name property because it uses get; set;, allowing changes after initialization. Although records are typically associated with immutability, they can be mutable if defined that way. In this case, changing p1.Name to "Bob" is valid and prints "Bob"
record Person(string Name);
record Employee(string Name) : Person(Name);
class Program
{
static void Main()
{
Person p1 = new Person("Alice");
Person p2 = new Employee("Alice");
Console.WriteLine(p1 == p2);
Console.WriteLine(ReferenceEquals(p1, p2));
}
}
- A: Complication error
- B: True, True
- C: False, False
- D: True, False
Answer
In this code, p1 == p2 returns False because even though both records have the same data, they are of different runtime types (Person vs Employee), and record equality checks for both value and type equality. ReferenceEquals(p1, p2) also returns False since they are two separate instances in memory. Record equality in C# requires both the type and the values of all properties to match exactly.
record Shape;
record Circle(double Radius) : Shape;
record Rectangle(double Width, double Height) : Shape;
class Program
{
static void Main()
{
Shape s = new Circle(5);
string result = s switch
{
Rectangle { Width: > 0, Height: > 0 } => "Rectangle",
Circle { Radius: > 10 } => "Large Circle",
Circle => "Small Circle",
_ => "Unknown"
};
Console.WriteLine(result);
}
}
- A: Rectangle
- B: Large Circle
- C: Small Circle
- D: Unknown
Answer
The switch expression uses pattern matching to check the runtime type and properties of a Shape instance. Since s is a Circle with radius 5, it doesn’t match the Radius > 10 case but matches the general Circle case, resulting in "Small Circle" being printed. This demonstrates property-based pattern matching and pattern ordering in C#.
class Program
{
static void Main()
{
int value = 5;
ModifyValue(ref value);
Console.WriteLine(value);
}
static void ModifyValue(ref int x)
{
x = 10;
try
{
x = 20;
throw new Exception();
}
catch
{
x = 30;
}
finally
{
x = 40;
}
x = 50;
}
}
- A: 10
- B: 20
- C: 50
- D: Exception thrown
Answer
The try block sets x to 20 and then throws an exception. The catch block catches the exception and sets x to 30. The finally block always runs, setting x to 40. After the finally block finishes, execution resumes after the entire try-catch-finally structure, so the line x = 50; runs and sets x to 50. Therefore, when ModifyValue finishes, x is 50, which is printed.
class Program
{
static void Main()
{
var result = Sum([5.0, 7.0 ]);
Console.WriteLine(result);
}
static double Sum(ReadOnlySpan<double> vector)
{
return vector switch
{
[] => 0,
[double x] => x,
[double x, double y] => x + y,
_ => vector.ToArray().Sum()
};
}
}
- A: 0
- B: 5
- C: 7
- D: 12
Answer
The pattern [double x, double y] matches because the input ReadOnlySpan contains exactly two elements. List patterns in C# match the structure and length of the input sequence; here, the compiler checks that the span has a length of 2 and binds the first element to x and the second to y. Since the input is [5.0, 7.0], this pattern is a perfect match and gets selected in the switch.
IEnumerable<string> Foo()
{
yield return "Foo1";
Console.WriteLine("Foo2");
}
foreach (var str in Foo())
Console.Write(str);
- A: Foo1Foo2
- B: Foo2Foo1
- C: Foo1
- D: Nothing
Answer
The foreach iterates fully over Foo(). It first prints "Foo1" (from yield return). Then the iterator resumes, executes Console.WriteLine("Foo2") (which prints "Foo2" on a new line). The combined output is "Foo1" immediately followed by "Foo2" on the next line, appearing as "Foo1Foo2" visually in the console.
var f = new Foo();
using (f)
{
Console.WriteLine(f.GetDispose());
}
Console.WriteLine(f.GetDispose());
public struct Foo : IDisposable
{
private bool dispose;
public void Dispose()
{
dispose = true;
}
public bool GetDispose()
{
return dispose;
}
}
- A: False, True
- B: False, False
- C: True, False
- D: Runtime error
Answer
In C#, when a struct is used in a using statement, it is boxed and copied into the scope of the using. The Dispose call happens on that copy, not the original f. Therefore, f.dispose remains false both inside and outside the using block.
class Program
{
private static object sobject = new();
private static void Write()
{
lock (sobject)
{
Console.WriteLine("test");
}
}
static void Main(string[] args)
{
lock (sobject)
{
Write();
}
}
}
- A: The program will hang (deadlock)
- B: Runtime error
- C: Complication error
- D: test
Answer
The lock statement uses a reentrant lock, meaning the same thread can acquire the lock multiple times without deadlocking. So, when Main locks sobject and calls Write(), which also locks sobject, it does not cause a deadlock. The program safely prints "test" once.
int c = 3;
Console.Write(Sum(5,3,out c));
Console.Write(c);
static int Sum(int a, int b, out int c)
{
return a + b;
}
- A: 8 3
- B: 8 8
- C: Complication error
- D: 5 3
Answer
The method Sum has an out parameter c, which must be assigned a value inside the method before control returns to the caller. However, in this code, the method returns a + b without assigning any value to c, which violates the rule for out parameters. As a result, the compiler will throw an error.
class Counter
{
public int Value;
public void Increment() => Value++;
}
class Program
{
static void Update(in Counter c)
{
c.Increment();
Console.Write(c.Value);
}
static void Main()
{
var counter = new Counter { Value = 5 };
Update(counter);
Console.Write(counter.Value);
}
}
- A: 55
- B: 66
- C: 56
- D: Complication error
Answer
The in modifier prevents reassignment of the parameter c, but since Counter is a reference type, its internal state can still be modified. c.Increment() increases the Value to 6, and both c.Value and counter.Value reflect that change. Hence, the output is 66.
try
{
var array = new int[] { 1, 2 };
Console.Write(array[5]);
}
catch(ApplicationException e)
{
Console.Write(1);
}
catch(SystemException e)
{
Console.Write(2);
}
catch(Exception e)
{
Console.Write(3);
}
- A: 1
- B: 2
- C: 3
- D: Runtime error
Answer
Accessing array[5] throws an IndexOutOfRangeException, which is a subclass of SystemException. Therefore, the SystemException catch block is executed, printing 2. The more general Exception block is skipped due to exception type specificity.
Foo<int>.Count++;
Console.WriteLine(Foo<double>.Count);
class Foo<T>
{
public static int Count;
}
- A: 0
- B: 1
- C: Complication Error
- D: Runtime error
Answer
Foo is a generic class, so each closed generic type (Foo, Foo, etc.) has its own separate static field. Incrementing Foo.Count does not affect Foo.Count, which remains at its default value of 0. Hence, the output is 0.
var numbers = GetNumbers();
var evenNumbers = numbers.Select(n => n * 2);
Console.WriteLine(evenNumbers.FirstOrDefault());
IEnumerable<int> GetNumbers()
{
yield return 1;
throw new Exception();
yield return 2;
}
- A: 0
- B: 2
- C: Exception is thrown
- D: Compilation error
Answer
The method GetNumbers() uses yield return, so it's lazily evaluated. FirstOrDefault() causes only the first value to be pulled from the iterator, which yields 1, and then 1 * 2 = 2 is returned. The throw new Exception() is never reached, so no exception is thrown.
var bar = new Bar { Foo = new Foo() };
bar.Foo.Change(5);
Console.WriteLine(bar.Foo.val);
public struct Foo
{
public int val;
public void Change(int newVal)
{
val = newVal;
}
}
public class Bar
{
public Foo Foo { get; set; }
}
- A: 0
- B: 5
- C: Exception is thrown
- D: Compilation error
Answer
In C#, structs are value types. When bar.Foo.Change(5) is called, it operates on a copy of the struct returned by the getter, not the actual Foo inside Bar. Therefore, the original Foo inside bar remains unchanged, and val stays 0.
var list = new
{
Items = new List<int> { 1, 2, 3, 4 }.GetEnumerator()
};
while (list.Items.MoveNext())
Console.WriteLine(list.Items.Current);
- A: Many 0
- B: 1 1 1 1
- C: Exception is thrown
- D: 1 2 3 4
Answer
List.Enumerator is a value type (a struct), and in this code, list.Items is accessed repeatedly inside the loop. Since list.Items returns a copy of the enumerator every time (due to value semantics of structs), each call to MoveNext() operates on a fresh, unadvanced copy. Therefore, MoveNext() always returns true, and Current always returns the default int value, which is 0. This causes an infinite loop of 0s being printed.
using System.Text;
class Program
{
static void Main()
{
var a = "XY";
var b = new StringBuilder().Append('X').Append('Y').ToString();
var c = string.Intern(b);
Console.WriteLine(a == b);
Console.WriteLine(a == c);
Console.WriteLine((object)a == (object)b);
Console.WriteLine((object)a == (object)c);
}
}
- A: False False True True
- B: True True False True
- C: False True False True
- D: False True True True
Answer
a == b: True because == checks value equality for strings. a == c: True, both are "XY", and interning ensures c points to the same string as a. (object)a == (object)b: False because b was constructed at runtime and is not interned; it's a different reference. (object)a == (object)c: True because string.Intern(b) returns the reference of the already interned string "XY" that a refers to.
object o = "hello";
A.Print(o);
class A
{
public static void Print<T>(T value) => Console.WriteLine("Generic");
public static void Print(string value) => Console.WriteLine("String");
}
- A: Exception is thrown
- B: Complication error
- C: Generic
- D: String
Answer
The variable o is declared as object, so the call Print(o) resolves to Print(T) with T inferred as object. Method overloading in C# is resolved at compile-time, and the actual runtime type of the object is not considered when selecting an overload. Even though the runtime type is string, the compiler chooses the generic overload because it matches the compile-time type.
class Program
{
static void Main()
{
Print(42);
}
static void Print<T>(T value) where T : struct
{
Console.WriteLine("Struct overload");
}
static void Print<T>(T value) where T : class
{
Console.WriteLine("Class overload");
}
}
- A: Struct overload
- B: Class overload
- C: Complication error
- D: Exception is thrown
Answer
C# does not allow overloads to differ only by generic constraints. The two methods are considered ambiguous in overload resolution, and result in a compile-time error regardless of which one is actually applicable for a given type at the call site.
Action[] actions = new Action[3];
for (int i = 0; i < 3; i++)
{
actions[i] = () => Console.Write(i);
}
Parallel.ForEach(actions, action => action());
- A: 333
- B: 123
- C: 321
- D: Non-deterministic
Answer
All three lambda expressions capture the same variable i by reference, not by value. By the time the parallel execution starts, the loop has completed and i has the value 3. So all three actions will print 3, resulting in output 333.
public partial class Sample
{
// A
partial void OnInitialized();
// B
partial void OnLoaded(string name = "default");
// C
partial int Calculate();
// D
partial void OnSaving();
}
- A
- B
- C
- D
Answer
Only C is invalid due to the non-void return type.
A partial method isn't required to have an implementing declaration in the following cases:
It doesn't have any accessibility modifiers (including the default private). It returns void. It doesn't have any out parameters. It doesn't have any of the following modifiers virtual, override, sealed, new, or extern.
using System.Buffers;
var pool = ArrayPool<byte>.Shared;
byte[] buffer1 = pool.Rent(4);
buffer1[0] = 42;
pool.Return(buffer1);
byte[] buffer2 = pool.Rent(4);
Console.WriteLine(buffer2[0]);
- A: 0
- B: 42
- C: Compilation error
- D: Runtime exception
Answer
When ArrayPool<T>.Return()
is called, it does not clear the array by default. So when the same array is rented again, it may still contain old data like 42
. To avoid this, you must return the array with clearArray: true
.
using System.Reflection;
class Mystery
{
private void Say(object x) => Console.Write("object ");
private void Say(string x) => Console.Write("string ");
}
class Program
{
static void Main()
{
var m = typeof(Mystery).GetMethod("Say", BindingFlags.NonPublic | BindingFlags.Instance);
var obj = new Mystery();
m.Invoke(obj, new object[] { "hi" });
}
}
- A: string
- B: object
- C: Compilation error
- D: Runtime exception
Answer
The call to GetMethod("Say", ...)
fails because there are two private overloads: one that takes object
and one that takes string
.
Since GetMethod
does not know which one to return, it throws an AmbiguousMatchException
.
To fix this, you must use the overload of GetMethod
that specifies parameter types explicitly.
class Program
{
static void Main()
{
var a = new { X = 1, Y = 2 };
var b = new { X = 1, Y = 2 };
Console.WriteLine(a==b);
Console.WriteLine(a.Equals(b));
}
}
- A: True and True
- B: False and True
- C: False and False
- D: True and False
Answer
Anonymous types in C# automatically override Equals()
and GetHashCode()
to compare property values.
So a.Equals(b)
returns true
because both objects have the same values for X
and Y
.
However, a == b
compares reference identity (i.e., are they the same object in memory?), which is false
since a
and b
are two separate instances.
class Data
{
public string this[int index] => "int";
public string this[string key] => "string";
}
class Program
{
static void Main()
{
var d = new Data();
Console.WriteLine(d['A']);
}
}
- A: int
- B: string
- C: Compilation error
- D: Runtime exception
Answer
In C#, when using an indexer like d['A']
, the character 'A'
is implicitly converted to its underlying int
value (Unicode code point 65).
Since there's an indexer that accepts an int
, that one is selected, and "int"
is printed.
using System.Linq.Expressions;
Expression<Func<int, int>> inner = z => z + 10;
Expression<Func<int, Func<int, int>>> outer =
y => x => inner.Compile()(x * y);
var compiled = outer.Compile();
Console.WriteLine(compiled(3)(2));
- A: 16
- B: 23
- C: 26
- D: Compilation error
Answer
The outer expression takes y
and returns a function that takes x
.
Inside that function, it compiles inner
(z => z + 10
) and applies it to x * y
.
When calling compiled(3)(2)
, x = 2
, y = 3
, so x * y = 6
.
The inner function then computes 6 + 10 = 16
.
Thus, the final output is 16
.
IAlpha a = new Delta();
IBeta b = new Delta();
a.Speak();
b.Speak();
interface IAlpha
{
void Speak() => Console.WriteLine("Alpha");
}
interface IBeta
{
void Speak() => Console.WriteLine("Beta");
}
class Delta : IAlpha, IBeta
{
// No Speak() implementation
}
- A: Alpha then Beta
- B: Compile-time error
- C: Runtime exception due to ambiguity
- D: Alpha twice
Answer
In C# 8.0+, default interface methods are dispatched based on the interface reference type at runtime.
a.Speak()
calls the default IAlpha.Speak()
→ prints "Alpha".
b.Speak()
calls the default IBeta.Speak()
→ prints "Beta".
Although Delta
implements both interfaces with default methods, there is no conflict until you try to call Speak()
on Delta
directly, which is not done here.
var obj = new Impl();
obj.Ping();
class Impl : IOne
{
// No explicit override of Ping
}
interface IOne
{
public void Ping() => Console.WriteLine("One");
}
- A: One
- B: Compile-time error: Ping is not implemented in Impl
- C: Runtime error: method not found
- D: Nothing is printed
Answer
Even though IOne
provides a default implementation of Ping()
, the method is not available via the implementing class directly.
Calling obj.Ping()
requires Impl
to provide an explicit implementation or for you to cast the object to the interface type.
To make it work, you could change the call to ((IOne)obj).Ping();
.
unsafe
{
Span<int> span = stackalloc int[3] { 1, 2, 3 };
fixed (int* ptr = span)
{
*(ptr + 1) = 42;
}
Console.WriteLine(string.Join(",", span.ToArray()));
}
- A: 1 42 3
- B: Compile-time error
- C: Runtime error
- D: 1 2 3
Answer
This code uses stackalloc
to allocate a Span<int>
on the stack with values {1, 2, 3}.
The fixed
statement pins the memory location of the span's underlying array, so a pointer can be used safely.
Then, the pointer is used to overwrite the second element (*(ptr + 1) = 42
).
As a result, the original span now contains [1, 42, 3], and that is printed.
class Program
{
static int counter = 0;
static void Main()
{
Thread t1 = new Thread(() => { for (int i = 0; i < 1000; i++) Interlocked.Increment(ref counter); });
Thread t2 = new Thread(() => { for (int i = 0; i < 1000; i++) Interlocked.Increment(ref counter); });
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine(counter);
}
}
- A: Between 1000 and 2000
- B: 1000
- C: 2000
- D: Unpredictable
Answer
The Interlocked.Increment
method performs an atomic operation, ensuring thread-safe incrementing of counter
.
Each thread runs 1000 increments, for a total of 2000.
Atomicity is guaranteed, so no race conditions occur, and the result is exactly 2000.
interface IProducer<out T>
{
T Produce();
}
class Animal { public virtual string Speak() => "Animal"; }
class Dog : Animal { public override string Speak() => "Woof"; }
class DogProducer : IProducer<Dog>
{
public Dog Produce() => new Dog();
}
class Program
{
static void Main()
{
IProducer<Dog> dogProducer = new DogProducer();
IProducer<Animal> animalProducer = dogProducer;
Console.WriteLine(animalProducer.Produce().Speak());
}
}
- A: Compilation error due to invalid assignment
- B: Runtime InvalidCastException
- C: "Woof"
- D: "Animal"
Answer
The IProducer<out T>
interface is covariant (out
), allowing IProducer<Dog>
to be assigned to IProducer<Animal>
.
This works because a Dog
is an Animal
, and out
ensures only reading (not writing) of type T.
The method Speak()
is overridden in Dog
, so "Woof"
is printed.
Covariance allows you to use a more derived type than originally specified.
In C#, it's supported in generic interfaces and delegates using the out
keyword, and it means you can assign something like IEnumerable<Dog>
to IEnumerable<Animal>
.
It works only for output positions (return values), ensuring type safety by allowing only reading, not writing.
Contravariance allows you to use a more generic (less derived) type than originally specified.
It's supported in generic interfaces and delegates using the in
keyword, meaning you can assign Action<Animal>
to Action<Dog>
.
Contravariance works only in input positions (method parameters), allowing safe writing but no reading.
struct Meter
{
public double Value;
public Meter(double value) => Value = value;
public static implicit operator Meter(double value) => new Meter(value);
public static implicit operator double(Meter m) => m.Value;
}
class Program
{
static void Main()
{
Meter m = 5;
double d = m + 2.5;
Console.WriteLine(d);
}
}
- A: 7.5
- B: Compilation error
- C: Runtime error
- D: 5
Answer
This code defines a struct
with implicit conversions between Meter
and double
.
When m + 2.5
is evaluated, m
is implicitly converted to double
, and regular arithmetic is performed.
The result 7.5
is printed, showing that both implicit operators work seamlessly.
Console.WriteLine(Math.Round(2.5));
Console.WriteLine(Math.Round(13.5));
- A: 2
14 - B: 3
13 - C: 3
14 - D: 2
13
Answer
By default, Math.Round(double)
uses "banker's rounding" (also called "round half to even").
This means values ending in .5
are rounded to the nearest even number.
So 2.5
rounds to 2
, and 13.5
rounds to 14
, but due to an error in option D, the correct output is actually:
Console.WriteLine(1 + 2 + 'A');
Console.WriteLine(1 + 'A' + 2);
Console.WriteLine('A' + 1 + 2);
- A: 68, 68, 68
- B: 68, 68, A12
- C: 68, A2, 65
- D: 3A, 1A2, A3
Answer
In C#, the 'A'
character has an ASCII value of 65
.
Integer and char
values are converted to int
during arithmetic operations.
1 + 2 + 'A'
→3 + 65 = 68
1 + 'A' + 2
→1 + 65 + 2 = 68
'A' + 1 + 2
→65 + 1 + 2 = 68
No string concatenation occurs, as all operands are numeric, so the result is pure integer addition.
interface IBase
{
void Hello() => Console.WriteLine("Hello from IBase");
}
interface IDerived : IBase
{
void IBase.Hello() => Console.WriteLine("Hello from IDerived");
}
class MyClass : IDerived { }
class Program
{
static void Main()
{
IDerived d = new MyClass();
((IBase)d).Hello();
}
}
- A: Hello from IBase
- B: Hello from IDerived
- C: Compile-time error
- D: Runtime exception
Answer
In C# 8.0 and later, interfaces can contain default implementations and even explicit reimplementations of members from other interfaces.
In this case:
IBase
defines a default implementation ofHello()
.IDerived
explicitly reimplementsIBase.Hello()
with a new body.
Even though the call is made through an IBase
reference, the runtime resolves it to IDerived
's explicit implementation because IDerived
overrides IBase.Hello()
explicitly.
Hence, it prints: Hello from IDerived
.
int value = 5;
Beta obj = new Beta();
obj.Process(value);
class Alpha
{
public void Process(int x)
{
Console.WriteLine("Process from Alpha");
}
}
class Beta : Alpha
{
public void Process(double y)
{
Console.WriteLine("Process from Beta");
}
}
- A: Process from Alpha
- B: Process from Beta
- C: Compile-time error
- D: Runtime exception
Answer
Although value
is an int, the base class method Process(int)
is hidden (not overridden) by Beta.Process(double)
.
Since Beta
defines a new method with the same name, overload resolution happens within Beta
, and int
is implicitly converted to double
.
Therefore, Beta.Process(double)
is called, and the output is "Process from Beta".
class Program
{
static async Task TestAsync()
{
Console.WriteLine("Before Yield");
await Task.Yield();
Console.WriteLine("After Yield");
}
static void Main()
{
var task = TestAsync();
Console.WriteLine("Main End");
task.Wait();
}
}
- A: Before Yield, Main End, After Yield
- B: Before Yield, After Yield, Main End
- C: Main End, Before Yield, After Yield
- D: Compilation error
Answer
Task.Yield()
causes the async method to yield back to the calling context, returning control to the caller immediately.
So:
"Before Yield"
is printed.- The method yields.
"Main End"
fromMain()
is printed next.- Then, when the continuation resumes,
"After Yield"
is printed.
This makes Task.Yield()
useful for breaking up long-running async code and avoiding blocking the thread.
var example = new Test(10);
var field = typeof(Test).GetField("ReadOnlyField");
field.SetValue(example, 20);
Console.WriteLine($"After applying reflection: {example.ReadOnlyField}");
public class Test
{
public readonly int ReadOnlyField;
public Test(int value)
{
ReadOnlyField = value;
}
}
- A: After applying reflection: 10
- B: After applying reflection: 20
- C: Runtime error
- D: Compilation error
Answer
It's possible to use Reflection to change the value of a readonly field, though doing so can lead to unexpected behavior and is generally not recommended as it violates the principle of immutability that readonly keyword is meant to enforce.
var s = new MyStruct();
using (s)
{
Console.WriteLine(s.GetDisposed());
}
Console.WriteLine(s.GetDisposed());
public struct MyStruct : IDisposable
{
private bool disposed;
public void Dispose()
{
disposed = true;
}
public bool GetDisposed()
{
return disposed;
}
}
- A: True, True
- B: False, True
- C: True, False
- D: False, False
Answer
When a struct
is passed to a using
block, it is copied because structs are value types.
So the Dispose()
method is called on the copy, not the original s
.
Therefore, disposed
inside the original s
remains false
both inside and outside the block.
int a = 0;
a += Increase();
Console.WriteLine(a);
int Increase()
{
a = a + 10;
return 1;
}
- A: 1
- B: 10
- C: 11
- D: Compilation error
Answer
The output is 1 because a += Increase() is evaluated as a = a + Increase(), where a is read as 0 before calling Increase(), which sets a = 10 internally but returns 1, and finally 0 + 1 is assigned back to a, overwriting the earlier change.
Foo(null);
Foo(null);
void Foo(object a)
{
Console.WriteLine("object");
}
void Foo(params object[] args)
{
Console.WriteLine("params");
}
- A: object, object
- B: object, params
- C: params, params
- D: Compilation error