Meta Description:Explore the power of type patterns in C# with this in-depth guide. Learn how to use Type, Declaration, and Var patterns for flexible type-checking and efficient code. Includes full examples, detailed explanations, and assignments at beginner, intermediate, and advanced levels to boost your coding skills.
In C#, patterns offer a flexible and powerful way to check and handle different types at runtime. Whether working with various subclasses, types, or complex objects, patterns allow for clean, type-safe code. In this article, we’ll explore three essential type patterns in C#: the Type Pattern, Declaration Pattern, and Var Pattern. Each section includes examples, detailed explanations, and assignments for practice at beginner, intermediate, and advanced levels.
1. Type Pattern
The Type Pattern allows us to perform a runtime type check on an object. This can be very useful when you need to identify a specific type or subclass and perform actions based on that type.
Example: Checking Object Types with the Type Pattern
Let’s consider a scenario where we have an object
type that could hold different values, such as int
, string
, or decimal
. The type pattern helps us check the actual type and act accordingly.
using System;
public class TypePatternExample
{
public static void Main()
{
object value = 42; // This could be any type, like int, string, or decimal.
switch (value)
{
case int i:
Console.WriteLine($"It's an integer: {i}");
break;
case string s:
Console.WriteLine($"It's a string: {s}");
break;
case decimal d:
Console.WriteLine($"It's a decimal: {d}");
break;
default:
Console.WriteLine("It's another type.");
break;
}
}
}
Explanation:
- Here, the
switch
statement uses the type pattern to check the actual type ofvalue
. - If
value
is of typeint
,string
, ordecimal
, the respective case is executed, displaying the type and value. - If
value
doesn’t match any specific type, thedefault
case executes, handling all other types.
2. Declaration Pattern
The Declaration Pattern allows you to check an object’s type and simultaneously create a local variable of that type. This is particularly helpful when you want to work with properties or methods specific to that type within a case block.
Example: Calculating Shipping Cost Based on Type
In this example, we’re calculating the shipping cost based on a specific type of ShippingProvider
. The pattern checks if the provider is of a specific type and applies different costs based on properties unique to each provider.
using System;
public abstract class ShippingProvider
{
public abstract decimal FreightCost { get; }
}
public class SwedishPostalServiceShippingProvider : ShippingProvider
{
public override decimal FreightCost => 50m;
public bool NextDayDelivery { get; set; }
}
public class FedexShippingProvider : ShippingProvider
{
public override decimal FreightCost => 30m;
public bool Priority { get; set; }
}
public class DeclarationPatternExample
{
public static decimal CalculateShippingCost(ShippingProvider provider) =>
provider switch
{
SwedishPostalServiceShippingProvider s when s.NextDayDelivery =>
s.FreightCost + 10m, // Extra cost for next-day delivery
SwedishPostalServiceShippingProvider s =>
s.FreightCost - 5m, // Discount for standard delivery
_ => 15m // Default cost for other providers
};
public static void Main()
{
var swedishProvider = new SwedishPostalServiceShippingProvider { NextDayDelivery = true };
var cost = CalculateShippingCost(swedishProvider);
Console.WriteLine($"Shipping cost: {cost}");
}
}
Explanation:
-
CalculateShippingCost
uses the declaration pattern to match against specific types and capture them as local variables. - When the provider is
SwedishPostalServiceShippingProvider
andNextDayDelivery
is true, an additional charge is applied. - If
NextDayDelivery
is false, a discount is applied to the cost. - The
default
case handles all other providers by setting a standard cost of 15.
3. Var Pattern
The Var Pattern matches any type, similar to a default
case. However, it also allows you to capture the matched variable for use, making it handy for handling unmatched cases without discarding the variable.
Example: Using the Var Pattern
The following example demonstrates using the var
pattern as a catch-all that still allows us to interact with unmatched types by capturing them in a variable.
using System;
public class VarPatternExample
{
public static string IdentifyShippingProvider(ShippingProvider provider) =>
provider switch
{
SwedishPostalServiceShippingProvider s when s.NextDayDelivery =>
"Next-day delivery with Swedish Postal Service",
FedexShippingProvider f when f.Priority =>
"Priority shipping with FedEx",
var other =>
$"Default shipping provider: {other.GetType().Name}"
};
public static void Main()
{
var fedexProvider = new FedexShippingProvider { Priority = true };
var result = IdentifyShippingProvider(fedexProvider);
Console.WriteLine(result);
var defaultProvider = new ShippingProvider();
result = IdentifyShippingProvider(defaultProvider);
Console.WriteLine(result);
}
}
Explanation:
- This example uses the
var
pattern to capture unmatched cases in a variable. - The first case handles
SwedishPostalServiceShippingProvider
withNextDayDelivery
. - The second case matches
FedexShippingProvider
withPriority
. - The
var other
pattern captures any otherShippingProvider
type, allowing us to print the provider’s type name as the default response.
Assignments
Beginner Level
Type Pattern: Modify the
TypePatternExample
to include a case forbool
. Add logic to print "It's a boolean" if the value is a boolean type.Declaration Pattern: Update the
CalculateShippingCost
function to add a discount if the provider isFedexShippingProvider
and thePriority
property isfalse
.Var Pattern: Modify the
IdentifyShippingProvider
method to add a check for an unsupported provider, returning “Unsupported provider type” for any unmatched cases.
Intermediate Level
Type Pattern: Extend the
TypePatternExample
to handle a customProduct
class with properties likeName
andPrice
. In thecase Product p
, print out the product’s name and price.Declaration Pattern: Create a
CalculateTax
method that uses the declaration pattern to calculate tax based on the specific type ofTaxableItem
(e.g.,FoodItem
,ElectronicsItem
), with different tax rates for each type.Var Pattern: Use the
var
pattern to capture any unhandled cases in theIdentifyShippingProvider
example. Print the provider type name in uppercase in the default case.
Advanced Level
Type Pattern: Modify the
TypePatternExample
to handle collections. If thevalue
is aList<int>
, calculate and print the sum of all integers. If it’s aList<string>
, print all strings concatenated together.Declaration Pattern: Update
CalculateShippingCost
to add a new provider,DHLShippingProvider
, that provides free shipping if theNextDayDelivery
property istrue
but applies a standard cost otherwise. Write additional logic for calculating the final cost.Var Pattern: In
IdentifyShippingProvider
, modify thevar
case to check if the captured provider has a methodGetShippingRate
. If it does, invoke that method and return the rate; otherwise, return "Provider rate not available."
Top comments (0)