CSharp Primer

πŸ”‘ Difference Between Classes, Structs, and Records

e.g., Class = DbContext, Struct = DateTime, Record = WeatherForecast in Web API

Aspect Class Struct Record (C# 9+)
Type Reference type Value type Reference type (by default, record struct makes it value type)
Memory Stored on heap, reference stored on stack Stored directly on stack (usually) Stored on heap (like class)
Default Equality Compares references (by default, unless overridden) Compares values field-by-field Compares values (structural equality built-in)
Inheritance Supports inheritance Cannot inherit (but can implement interfaces) Supports inheritance (like classes)
Immutability Mutable by default Mutable by default Designed for immutability (with-expressions, init-only setters)
Performance Slower for small data (heap allocation, GC overhead) Faster for small/lightweight data (stack allocation) Similar to classes (heap allocation), but concise equality & immutability make them ideal for data models
Use Case Complex objects, OOP with polymorphism Small, lightweight objects (like coordinates, color, point) Data-centric models (DTOs, records in DDD, immutable states)

Code Examples

πŸ”Ή Class

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
var p1 = new Person { Name = "Alice", Age = 30 };
var p2 = new Person { Name = "Alice", Age = 30 };

Console.WriteLine(p1 == p2); // False (different references)

πŸ”Ή Struct

struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}
var p1 = new Point { X = 5, Y = 10 };
var p2 = new Point { X = 5, Y = 10 };

Console.WriteLine(p1.Equals(p2)); // True

πŸ”Ή Record

public record PersonRecord(string Name, int Age);
var r1 = new PersonRecord("Alice", 30);
var r2 = new PersonRecord("Alice", 30);

Console.WriteLine(r1 == r2); // True (structural equality)
var r3 = r1 with { Age = 31 };  
Console.WriteLine(r3); // PersonRecord { Name = Alice, Age = 31 }

βœ… Quick Rules of Thumb


πŸ‘‰ In short:


Great one πŸ‘ β€” this is a classic interview trap: ref vs out in C#.

Let’s carefully break it down:


πŸ”‘ Difference Between ref and out in C#

Aspect ref out
Initialization before call Variable must be initialized before passing. Variable does not need to be initialized before passing.
Assignment inside method Optional β€” method may or may not assign a new value. Mandatory β€” method must assign a value before returning.
Purpose Use when the method needs to read and modify the caller’s variable. Use when the method’s job is to output a value back to the caller.
Data flow Two-way (input + output). One-way (output only).
Common usage Updating existing values, performance optimizations (large structs). Methods that return multiple values (before Tuple / ValueTuple).

Code Example: ref

using System;

class Program
{
    static void DoubleValue(ref int number)
    {
        number = number * 2;   // modifies caller's value
    }

    static void Main()
    {
        int x = 5;  // must be initialized
        DoubleValue(ref x);
        Console.WriteLine(x); // Output: 10
    }
}

Code Example: out

using System;

class Program
{
    static void Divide(int dividend, int divisor, out int quotient, out int remainder)
    {
        quotient = dividend / divisor;   // must assign
        remainder = dividend % divisor;
    }

    static void Main()
    {
        int q, r;  // uninitialized is OK
        Divide(10, 3, out q, out r);
        Console.WriteLine($"Quotient = {q}, Remainder = {r}");  
        // Output: Quotient = 3, Remainder = 1
    }
}

βœ… Summary


real .NET framework example (like Int32.TryParse(string, out int) which uses out)?


πŸ”‘ Enums

An Enum (short for enumeration) is a special value type in C# that lets you define a set of named constants for better readability and maintainability.

πŸ‘‰ Instead of using raw numbers (magic numbers), you use meaningful names.


Declaring an Enum

enum Weekday
{
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}

By default:


Custom Values

You can explicitly assign values:

enum ErrorCode
{
    None = 0,
    NotFound = 404,
    ServerError = 500,
    Unauthorized = 401
}

Usage Example

class Program
{
    static void Main()
    {
        Weekday today = Weekday.Monday;

        if (today == Weekday.Monday)
        {
            Console.WriteLine("Start of the week!");
        }

        // Casting to int
        int dayValue = (int)Weekday.Friday;
        Console.WriteLine(dayValue);  // Output: 4

        // Casting from int
        Weekday w = (Weekday)6;
        Console.WriteLine(w);  // Output: Sunday
    }
}

Enum Methods

C# provides built-in helpers:

foreach (var day in Enum.GetValues(typeof(Weekday)))
{
    Console.WriteLine(day);
}
// Output: Monday Tuesday ... Sunday

Flags Enum (Bitwise Enums)

[Flags]
enum FileAccess
{
    None = 0,
    Read = 1,
    Write = 2,
    Execute = 4
}

class Program
{
    static void Main()
    {
        FileAccess access = FileAccess.Read | FileAccess.Write;
        Console.WriteLine(access); // Output: Read, Write

        bool canWrite = access.HasFlag(FileAccess.Write);
        Console.WriteLine(canWrite); // Output: True
    }
}

βœ… Summary


πŸ‘‰ In short:

real .NET example where Enums are used (like System.DayOfWeek or ConsoleColor)?

πŸ‘ Converting a string to enum is very common in C#.

Here are the main ways:


πŸ”‘ 1. Using Enum.Parse

enum Weekday
{
    Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}

class Program
{
    static void Main()
    {
        string input = "Friday";

        Weekday day = (Weekday)Enum.Parse(typeof(Weekday), input);
        Console.WriteLine(day);  // Output: Friday
    }
}

⚠️ If the string doesn’t match, it throws an exception.


πŸ”‘ 2. Using Enum.TryParse (Safe way)

string input = "Friday";

if (Enum.TryParse<Weekday>(input, out var day))
{
    Console.WriteLine(day);  // Output: Friday
}
else
{
    Console.WriteLine("Invalid enum value!");
}

βœ… No exception β†’ returns false if the string is invalid.


πŸ”‘ 3. Case-insensitive Parsing

string input = "friday";

Weekday day = (Weekday)Enum.Parse(typeof(Weekday), input, ignoreCase: true);
Console.WriteLine(day);  // Output: Friday

With TryParse:

Enum.TryParse("friday", true, out Weekday day);
Console.WriteLine(day); // Output: Friday

βœ… Summary


πŸ‘‰ Example:


πŸ”‘ ** Equals(object obj)**

Example:

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public override bool Equals(object obj)
    {
        if (obj is Person other)
        {
            return this.Name == other.Name && this.Age == other.Age;
        }
        return false;
    }
}

Usage:

var p1 = new Person { Name = "Alice", Age = 30 };
var p2 = new Person { Name = "Alice", Age = 30 };

Console.WriteLine(p1.Equals(p2)); // True (because we overrode Equals)

πŸ”‘ ** GetHashCode()**

Example:

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public override bool Equals(object obj)
    {
        if (obj is Person other)
        {
            return this.Name == other.Name && this.Age == other.Age;
        }
        return false;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(Name, Age); // .NET built-in helper
    }
}

Usage:

var set = new HashSet<Person>();
set.Add(new Person { Name = "Alice", Age = 30 });
set.Add(new Person { Name = "Alice", Age = 30 });

Console.WriteLine(set.Count); // 1 (because Equals + GetHashCode match)

βœ… Rules to Remember

  1. If you override Equals(), you must also override GetHashCode().

    • Otherwise, two objects considered equal may produce different hash codes, breaking collections like Dictionary.
  2. Consistency:

    • Equal objects β†’ same hash code.

    • Unequal objects β†’ can have same or different hash codes.

  3. Use HashCode.Combine(...) (C# 8+) or a good hash algorithm for combining multiple fields.


Real-life Analogy


πŸ‘‰ In short:


πŸ”‘ Difference Between ==, Equals(), and ReferenceEquals()

Operator/Method Defined In Default Behavior Can be Overridden? Typical Use
== Operator (can be overloaded) For reference types: compares references (same memory). For value types: compares values. βœ… Yes, operator overloading Flexible equality checks
Equals() System.Object Reference equality (for reference types). Value comparison (for value types like int). βœ… Yes (commonly overridden) Define custom equality logic
ReferenceEquals() System.Object (static method) Always checks if two references point to the same object in memory. ❌ No Identity check (ignores overrides)

1. Using ==

string s1 = "hello";
string s2 = "hello";

Console.WriteLine(s1 == s2); // True (string overrides == to compare values)

2. Using Equals()

object o1 = "hello";
object o2 = "hello";

Console.WriteLine(o1.Equals(o2)); // True (string overrides Equals to compare content)

3. Using ReferenceEquals()

string a = "hello";
string b = string.Copy(a);

Console.WriteLine(Object.ReferenceEquals(a, b)); // False (different objects in memory)
Console.WriteLine(a == b);                       // True  (content is same)
Console.WriteLine(a.Equals(b));                  // True  (content is same)

βœ… Quick Rules


Real-life Analogy


πŸ‘‰ In short:



TimeSpan Struct -