Fundamentals of C# Programming
Welcome to the Fundamentals of C# Programming course. This module is designed for beginners and intermediate developers who want to solidify their understanding of core C# concepts such as…

In C#, what is the effect of declaring a parameter with the keyword 'ref'?
Which of the following statements about C# structs is true?
What will be the output of the following code? int i = 5; Console.WriteLine(i == 5 ? "Five" : "Not Five");
Which interface must a class implement to be usable with a 'foreach' loop without explicitly providing an enumerator?
When overriding a virtual method in a derived class, which keyword must be used in the method declaration?
What is the purpose of the 'using' directive in a C# source file?
Which of the following correctly describes the behavior of the 'goto default;' statement inside a switch block?
What is the result of casting a double value 3.7 to an int using '(int) d' in C#?
Which access modifier makes a class member visible only to derived classes and classes within the same assembly?
Introduction to C# Fundamentals
Welcome to the Fundamentals of C# Programming course. This module is designed for beginners and intermediate developers who want to solidify their understanding of core C# concepts such as static members, reference parameters, structs, conditional operators, enumeration, method overriding, using directives, and switch‑case flow control. By the end of this lesson you will be able to write clean, efficient C# code and explain why each language feature behaves the way it does.
Static Members: Calling Methods Without an Instance
What is a static method?
A static method belongs to the class itself rather than to any particular object. Because it does not rely on instance data, you can invoke it directly using the class name:
public class MathHelper {
public static int Add(int a, int b) => a + b;
}
int result = MathHelper.Add(3, 4); // No "new MathHelper()" required
The keyword static is the only one that enables this behavior. Other modifiers such as sealed, abstract, or public do not affect how a method is accessed.
- When to use static methods: utility functions, factory methods, or any operation that does not need to maintain state.
- Benefits: reduced memory footprint, easier testing, and clear intent that the method is independent of object state.
Reference Parameters with ref
Passing arguments by reference
In C#, parameters are passed by value by default. Adding the ref keyword changes this default behavior, allowing the method to modify the caller’s variable directly.
void Increment(ref int number) {
number += 1; // Modifies the original variable
}
int count = 5;
Increment(ref count);
Console.WriteLine(count); // Outputs 6
Key points about ref:
- The argument must be initialized before it is passed.
- Both the caller and the callee see the same storage location, so changes are reflected immediately.
refis different fromout, which does not require the variable to be initialized but mandates assignment inside the method.
Understanding Structs
Value types vs. reference types
A struct in C# is a value type. This means that when you assign a struct to a new variable or pass it to a method, the entire value is copied, not just a reference.
struct Point {
public int X;
public int Y;
}
Point p1 = new Point { X = 1, Y = 2 };
Point p2 = p1; // Copies the values
p2.X = 10;
Console.WriteLine(p1.X); // Still 1
Because structs are stored on the stack (or inline within other objects), they are ideal for small, immutable data structures. Contrary to a common misconception, structs can contain methods, properties, and even interfaces; they simply cannot inherit from another struct or class.
- When to choose a struct: lightweight data containers, points, colors, or any type that represents a single value.
- When to avoid a struct: large objects, mutable data that requires polymorphism, or when you need inheritance.
Conditional (Ternary) Operator
Compact decision making
The ternary operator ?: provides a concise way to evaluate a Boolean expression and return one of two values.
int i = 5;
Console.WriteLine(i == 5 ? "Five" : "Not Five"); // Prints "Five"
In the example above, the expression i == 5 evaluates to true, so the first operand ("Five") is selected. This operator is especially useful for simple inline checks, but for more complex logic a full if/else block is recommended for readability.
Enumeration with IEnumerable
Making a class foreach‑compatible
To enable the foreach syntax, a class must implement the IEnumerable interface (or its generic counterpart IEnumerable<T>). This interface requires a GetEnumerator method that returns an IEnumerator.
public class SimpleCollection : IEnumerable {
private int[] _items = {1, 2, 3};
public IEnumerator GetEnumerator() => ((IEnumerable)_items).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
foreach (var n in new SimpleCollection()) {
Console.WriteLine(n);
}
Implementing IEnumerable abstracts the iteration logic, allowing the consumer to use foreach without worrying about the underlying data structure.
Method Overriding and the override Keyword
Polymorphism in derived classes
When a base class declares a method as virtual, a derived class can provide its own implementation using the override keyword. This tells the compiler that the method replaces the base implementation at runtime.
class Animal {
public virtual void Speak() => Console.WriteLine("Animal sound");
}
class Dog : Animal {
public override void Speak() => Console.WriteLine("Woof!");
}
Animal a = new Dog();
a.Speak(); // Outputs "Woof!"
Without override, the method would hide the base version (using the new keyword) rather than participate in polymorphic dispatch.
The using Directive
Simplifying namespace access
The using directive at the top of a C# file allows you to reference types without fully qualifying their namespace each time. For example:
using System.Text;
StringBuilder sb = new StringBuilder(); // No need for System.Text.StringBuilder
Note that this directive does **not** import DLLs or manage resource disposal; those tasks are handled by project references and the using statement block, respectively. Its primary purpose is to improve code readability and reduce typing.
Switch Statements and goto default
Control flow within a switch block
The goto default; statement explicitly transfers execution to the default case label inside the same switch. This can be useful when multiple case blocks share common fallback logic.
int value = 3;
switch (value) {
case 1:
Console.WriteLine("One");
break;
case 2:
Console.WriteLine("Two");
break;
default:
Console.WriteLine("Other");
break;
case 3:
goto default; // Jumps to the default case
}
Using goto default does **not** cause a compilation error; it simply redirects the flow to the default label, ensuring consistent handling for specific cases that require the same outcome.
Summary and Best Practices
We have covered eight essential C# concepts:
- Static methods – call without an instance.
- ref parameters – pass arguments by reference.
- Structs – value types that are copied on assignment.
- Ternary operator – concise conditional expression.
- IEnumerable – enables
foreachloops. - override keyword – implements polymorphic behavior.
- using directive – simplifies namespace usage.
- goto default – transfers control to the default case in a switch.
Remember to apply each feature in the context where it adds clarity and performance. Overusing static members can lead to tightly coupled code, while misusing ref may make debugging harder. Choose structs for small, immutable data, and always implement IEnumerable when you want your collection to be iterable.
By mastering these fundamentals, you lay a strong foundation for more advanced topics such as async programming, LINQ, and design patterns in C#.
