C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
175: The is and as Operators
I've seen this happen in almost every junior developer's first big project. You're building a system with a base class—let's say a game inventory—and you start adding specialized items. Everything works great until you try to treat a generic item as a specific one without checking first. Then, the whole application crashes with an InvalidCastException.
public class Item { public string Name { get; set; } }
public class Weapon : Item { public int Damage { get; set; } }
public class Potion : Item { public int HealAmount { get; set; } }
public class InventoryManager
{
public void PrintItemDetails(Item item)
{
// This is the "danger zone"
Weapon weapon = (Weapon)item;
Console.WriteLine($"Weapon Damage: {weapon.Damage}");
}
}
// This works fine:
manager.PrintItemDetails(new Weapon { Name = "Iron Sword", Damage = 10 });
// This crashes the program:
manager.PrintItemDetails(new Potion { Name = "Health Potion", HealAmount = 20 });
The Danger of the Hard Cast
The problem here is the "hard cast"—that (Weapon)item line. You're essentially telling the C# compiler, "I know for a fact this is a Weapon; just force it." When you pass in a Potion, the runtime realizes a potion isn't a weapon and throws an exception. In a real production environment, this is how you get 2:00 AM bug reports.
Safe Checking with Type Pattern Matching
The modern way to handle this is using the is operator. In older versions of C#, is just returned a boolean. But now, we have "pattern matching," which lets you check the type and create a new variable all in one go. It's significantly cleaner.
public void PrintItemDetails(Item item)
{
if (item is Weapon weapon)
{
// 'weapon' is only available inside this block
Console.WriteLine($"Weapon Damage: {weapon.Damage}");
}
else if (item is Potion potion)
{
Console.WriteLine($"Potion Healing: {potion.HealAmount}");
}
else
{
Console.WriteLine($"Generic Item: {item.Name}");
}
}
I personally prefer this approach because it's explicit. You're asking a question ("Is this a weapon?") and if the answer is yes, C# gives you a typed variable to work with immediately. No double-casting required.
The 'Try and See' Approach with the as Operator
Then there's the as operator. While is is like asking a question, as is more like an attempt. It tries to cast the object to the specified type. If it succeeds, you get the object; if it fails, it doesn't throw an exception—it simply returns null.
public void ApplyWeaponBonus(Item item)
{
Weapon weapon = item as Weapon;
if (weapon != null)
{
Console.WriteLine($"Applying bonus to {weapon.Name}!");
}
}
You might wonder why you'd use as when is pattern matching exists. The as operator is still incredibly useful when you're dealing with a sequence of operations where a null result is a natural part of the logic, or when you want to perform the cast once and use the resulting variable across a larger block of code without being nested inside an if statement.
Just remember: never use as with value types (like int or bool) because they can't be null. Stick to reference types or nullable types.
📋 Practical Task
Implement a Smart NPC Dialogue Handler
You are building a dialogue system where different types of NPCs provide different kinds of information. You have a base NPC class and two subclasses: QuestGiver (which has a QuestName property) and Merchant (which has a StoreInventoryCount property).
Your task is to complete the DialogueManager class. Write a method called InteractWithNPC(NPC npc) that uses the is operator with pattern matching to:
- If the NPC is a
QuestGiver, print: "Quest available: [QuestName]" - If the NPC is a
Merchant, print: "Items in stock: [StoreInventoryCount]" - If the NPC is neither, print: "Hello there, traveler!"
Then, create a list containing one of each NPC type and loop through them, calling InteractWithNPC for each one to ensure your logic handles all types without crashing.
There are no comments for now.