๐ฏ Best Practices: C# Common Types
The Toolbox Analogy: Imagine you have a magical toolbox. Each tool inside does ONE thing really well. Today, we meet the most important tools every C# builder needs!
๐ DateTime and TimeSpan: Your Time Travelers
What is DateTime?
Think of DateTime like a snapshot of a moment. It captures the exact year, month, day, hour, minute, and second.
Like a photo timestamp! ๐ธ
// Right now!
DateTime now = DateTime.Now;
// A specific moment
DateTime birthday = new DateTime(2024, 6, 15);
// Just today's date
DateTime today = DateTime.Today;
What is TimeSpan?
TimeSpan is the distance between two moments. Like measuring how long a movie is!
// 2 hours and 30 minutes
TimeSpan movieLength =
new TimeSpan(2, 30, 0);
// Difference between dates
TimeSpan age = DateTime.Now - birthday;
Console.WriteLine(age.Days);
๐ Best Practices
| โ Do This | โ Avoid This |
|---|---|
Use DateTime.UtcNow for servers |
DateTime.Now across time zones |
Parse with TryParse |
Direct Parse (crashes on bad input) |
Compare with DateTime.Compare |
Using == for approximate times |
// GOOD: Safe parsing
if (DateTime.TryParse("2024-06-15", out var date))
{
Console.WriteLine(date);
}
๐ Guid: Your Unique ID Generator
What is a Guid?
A Guid (Globally Unique Identifier) is like giving every snowflake its own name. No two are ever the same!
// Create a brand new unique ID
Guid id = Guid.NewGuid();
// Output: 3f2504e0-4f89-11d3-9a0c-0305e82c3301
Why Use Guids?
Imagine a school with 1 million students. Numbers might repeat. But Guids? Never!
// Database record ID
public class User
{
public Guid Id { get; set; } = Guid.NewGuid();
public string Name { get; set; }
}
๐ Best Practices
// โ
Parse safely
if (Guid.TryParse("...", out Guid parsed))
{
// Use parsed
}
// โ
Check for empty
if (userId == Guid.Empty)
{
Console.WriteLine("No ID set!");
}
// โ Never compare with strings directly
// Use Guid.Parse or TryParse first
๐งฎ Math Class: Your Calculator Friend
The Math class is your built-in calculator. It handles all the hard math!
Common Operations
// Absolute value (always positive)
Math.Abs(-5); // Returns: 5
// Round numbers
Math.Round(3.7); // Returns: 4
Math.Floor(3.9); // Returns: 3
Math.Ceiling(3.1); // Returns: 4
// Power and roots
Math.Pow(2, 3); // 2ยณ = 8
Math.Sqrt(16); // โ16 = 4
// Min and Max
Math.Max(5, 10); // Returns: 10
Math.Min(5, 10); // Returns: 5
๐ Best Practices
// โ
Use Math.Round with MidpointRounding
Math.Round(2.5, MidpointRounding.AwayFromZero);
// Returns: 3
// โ
Clamp values to a range
int value = Math.Clamp(score, 0, 100);
// โ Don't divide by zero
// Check first!
if (divisor != 0)
{
result = Math.Floor(number / divisor);
}
๐ฒ Random Class: Your Dice Roller
Random generates unpredictable numbers. Like rolling dice!
Basic Usage
Random random = new Random();
// Number between 0 and 9
int dice = random.Next(10);
// Number between 1 and 6 (like real dice)
int realDice = random.Next(1, 7);
// Decimal between 0.0 and 1.0
double chance = random.NextDouble();
๐ Best Practices
// โ
Create ONE Random instance and reuse it
private static readonly Random _random =
new Random();
// โ DON'T create new Random in loops
// They might give same numbers!
for (int i = 0; i < 10; i++)
{
// BAD: var r = new Random();
// GOOD: Use the shared _random
Console.WriteLine(_random.Next(100));
}
graph TD A["Create Random Once"] --> B["Reuse Everywhere"] B --> C["Random Numbers!"] D["Create Random in Loop"] --> E["Same Seed"] E --> F["Same Numbers ๐ข"]
โ๏ธ IEquatable and IComparable: The Comparison Twins
IEquatable: โAre we the same?โ
Tells C# how to check if two objects are equal.
public class Student : IEquatable<Student>
{
public int Id { get; set; }
public string Name { get; set; }
public bool Equals(Student other)
{
if (other == null) return false;
return Id == other.Id;
}
}
IComparable: โWho comes first?โ
Tells C# how to sort objects.
public class Student : IComparable<Student>
{
public int Id { get; set; }
public int CompareTo(Student other)
{
if (other == null) return 1;
return Id.CompareTo(other.Id);
}
// Returns: negative (before), 0 (same), positive (after)
}
๐ Best Practices
| Interface | Use When |
|---|---|
IEquatable<T> |
Checking if objects match |
IComparable<T> |
Sorting lists |
// Now you can sort students!
List<Student> students = GetStudents();
students.Sort(); // Uses CompareTo
๐ GetHashCode and Equals: The Identity Twins
Why Do They Matter?
When you put objects in a Dictionary or HashSet, C# needs two things:
- GetHashCode: A quick number to find the bucket
- Equals: Confirm itโs really the same object
Think of it like a library:
- HashCode = Which shelf to look at
- Equals = Check the exact book title
The Golden Rule
If two objects are Equal, they MUST have the same HashCode!
public class Book
{
public string ISBN { get; set; }
public override bool Equals(object obj)
{
if (obj is Book other)
return ISBN == other.ISBN;
return false;
}
public override int GetHashCode()
{
return ISBN?.GetHashCode() ?? 0;
}
}
๐ Best Practices
// โ
Use HashCode.Combine for multiple fields
public override int GetHashCode()
{
return HashCode.Combine(FirstName, LastName, Age);
}
// โ
Always override BOTH together
// โ Never override just one!
graph TD A["Override Equals?"] -->|Yes| B["Must Override GetHashCode!"] B --> C["Same objects = Same hash"] A -->|No| D["Use defaults"]
๐ญ Equals vs == Operator: Know the Difference!
The Key Difference
| Feature | == Operator |
.Equals() Method |
|---|---|---|
| For reference types | Checks same memory location | Checks content equality |
| For value types | Checks values | Checks values |
| Can be null? | Left side can be null | Calling on null = crash! |
Visual Example
string a = "hello";
string b = "hello";
string c = a;
// Reference comparison
object.ReferenceEquals(a, b); // Might be true (string interning)
object.ReferenceEquals(a, c); // True (same reference)
// Value comparison
a.Equals(b); // True (same content)
a == b; // True (strings compare by value)
๐ Best Practices
// โ
For null-safe comparison
if (string.Equals(str1, str2,
StringComparison.OrdinalIgnoreCase))
{
// Case-insensitive match!
}
// โ
Use == for value types
int x = 5, y = 5;
if (x == y) { } // Perfect!
// โ
For objects, prefer Equals()
if (person1.Equals(person2)) { }
// โ Avoid .Equals() on potentially null objects
// Use null-conditional or static Equals
if (person1?.Equals(person2) == true) { }
Special Case: Custom Classes
public class Point
{
public int X { get; set; }
public int Y { get; set; }
// Override == for value-like behavior
public static bool operator ==(Point a, Point b)
{
if (a is null) return b is null;
return a.Equals(b);
}
public static bool operator !=(Point a, Point b)
{
return !(a == b);
}
}
๐ Quick Summary
graph TD A["Common Types"] --> B["DateTime/TimeSpan"] A --> C["Guid"] A --> D["Math"] A --> E["Random"] A --> F["IEquatable/IComparable"] A --> G["GetHashCode/Equals"] A --> H["Equals vs =="] B --> B1["Moments & Durations"] C --> C1["Unique IDs"] D --> D1["Calculations"] E --> E1["Randomness"] F --> F1["Compare Objects"] G --> G1["Collections/Dictionaries"] H --> H1["Reference vs Value"]
๐ก Remember!
- DateTime: Use UTC for servers, TryParse for safety
- Guid: Perfect for unique IDs, never repeats
- Math: Your calculator, use Clamp for ranges
- Random: Create once, reuse always
- IEquatable: โAre we the same?โ
- IComparable: โWho comes first?โ
- GetHashCode: Always pair with Equals
- == vs Equals: Know when each applies!
๐ Youโre now equipped with the essential C# tools. Use them wisely!
