C# & ASP.NET Topic

C# Advanced Features

1. Records

What are Records?

Records are immutable reference types that provide value-based equality semantics. Perfect for data carriers where identity is based on content.

Detailed Examples:

Basic Record:

csharp

// Simple record - immutable by default
public record Person(string FirstName, string LastName, int Age);

// Equivalent traditional class would be much more code!
public class TraditionalPerson
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
    public int Age { get; init; }
    
    public TraditionalPerson(string firstName, string lastName, int age)
    {
        FirstName = firstName;
        LastName = lastName;
        Age = age;
    }
    
    // Would need to override Equals, GetHashCode, ToString, etc.
}

// Usage
public class RecordDemo
{
    public static void DemonstrateBasicRecords()
    {
        // Creation with positional parameters
        var person1 = new Person("John", "Doe", 30);
        var person2 = new Person("John", "Doe", 30);
        var person3 = new Person("Jane", "Smith", 25);
        
        // Value-based equality
        Console.WriteLine($"person1 == person2: {person1 == person2}"); // True
        Console.WriteLine($"person1 == person3: {person1 == person3}"); // False
        
        // Built-in ToString()
        Console.WriteLine(person1); // Person { FirstName = John, LastName = Doe, Age = 30 }
        
        // Deconstruction
        var (firstName, lastName, age) = person1;
        Console.WriteLine($"First: {firstName}, Last: {lastName}, Age: {age}");
        
        // With-expressions for non-destructive mutation
        var olderPerson = person1 with { Age = 31 };
        Console.WriteLine(olderPerson); // Age is now 31
    }
}

Advanced Record Features:

csharp

// Record with additional members
public record Employee(string FirstName, string LastName, int Age)
{
    // Additional properties
    public string FullName => $"{FirstName} {LastName}";
    public string Email => $"{FirstName.ToLower()}.{LastName.ToLower()}@company.com";
    
    // Methods
    public bool IsSenior() => Age >= 40;
    
    // Validation in constructor
    public Employee : this(FirstName, LastName, Age)
    {
        if (Age < 18)
            throw new ArgumentException("Employee must be at least 18 years old");
    }
}

// Record structs (C# 10+)
public readonly record struct Point(int X, int Y)
{
    public double DistanceFromOrigin => Math.Sqrt(X * X + Y * Y);
    
    public static Point operator +(Point a, Point b) => 
        new Point(a.X + b.X, a.Y + b.Y);
}

// Inheritance with records
public abstract record Shape(string Color);
public record Circle(string Color, double Radius) : Shape(Color);
public record Rectangle(string Color, double Width, double Height) : Shape(Color);

public class AdvancedRecordDemo
{
    public static void DemonstrateAdvancedRecords()
    {
        // Record with additional members
        var emp = new Employee("Alice", "Johnson", 28);
        Console.WriteLine(emp.FullName); // Alice Johnson
        Console.WriteLine(emp.Email);    // alice.johnson@company.com
        Console.WriteLine(emp.IsSenior()); // False
        
        // Record struct
        var point1 = new Point(3, 4);
        var point2 = new Point(1, 2);
        var point3 = point1 + point2;
        Console.WriteLine($"Point: {point3}, Distance: {point3.DistanceFromOrigin}");
        
        // Inheritance
        Shape circle = new Circle("Red", 5.0);
        Shape rectangle = new Rectangle("Blue", 10.0, 5.0);
        
        Console.WriteLine(circle); // Circle { Color = Red, Radius = 5 }
        
        // Pattern matching with records (we'll cover this next)
        if (circle is Circle { Radius: > 3.0 } largeCircle)
        {
            Console.WriteLine($"Large circle with radius: {largeCircle.Radius}");
        }
    }
}

2. Pattern Matching

Pattern matching provides a concise way to test values and extract information.

Detailed Examples:

Basic Pattern Matching:

csharp

public class PatternMatchingDemo
{
    public static void DemonstrateBasicPatterns()
    {
        object[] data = { 42, "Hello", 3.14, null, "World", 100, new int[] { 1, 2, 3 } };
        
        foreach (var item in data)
        {
            // Type patterns
            string result = item switch
            {
                // Constant pattern
                null => "Null value",
                
                // Type pattern with condition
                int i when i > 50 => $"Large integer: {i}",
                int i => $"Integer: {i}",
                
                // Type pattern
                string s when s.Length > 5 => $"Long string: {s}",
                string s => $"String: {s}",
                
                // Property pattern (C# 8+)
                double d => $"Double: {d}",
                
                // Discard pattern
                _ => "Unknown type"
            };
            
            Console.WriteLine(result);
        }
    }
    
    public static void PropertyPatterns()
    {
        var people = new Person[]
        {
            new Person("John", "Doe", 25),
            new Person("Jane", "Smith", 35),
            new Person("Bob", "Johnson", 45),
            new Person("Alice", "Williams", 28)
        };
        
        foreach (var person in people)
        {
            var category = person switch
            {
                { Age: < 30 } => "Young",
                { Age: >= 30 and < 40 } => "Middle-aged",
                { Age: >= 40 } => "Senior",
                _ => "Unknown"
            };
            
            Console.WriteLine($"{person.FirstName} is {category}");
        }
    }
    
    public static void TuplePatterns()
    {
        var gameStates = new[] { ("rock", "scissors"), ("paper", "rock"), ("scissors", "paper"), ("rock", "rock") };
        
        foreach (var (player1, player2) in gameStates)
        {
            var result = (player1, player2) switch
            {
                ("rock", "scissors") => "Player 1 wins",
                ("paper", "rock") => "Player 1 wins",
                ("scissors", "paper") => "Player 1 wins",
                ("scissors", "rock") => "Player 2 wins",
                ("rock", "paper") => "Player 2 wins",
                ("paper", "scissors") => "Player 2 wins",
                (_, _) when player1 == player2 => "Tie",
                _ => "Invalid move"
            };
            
            Console.WriteLine($"{player1} vs {player2}: {result}");
        }
    }
}

Advanced Pattern Matching:

csharp

public abstract record Shape(string Color);
public record Circle(string Color, double Radius) : Shape(Color);
public record Rectangle(string Color, double Width, double Height) : Shape(Color);
public record Triangle(string Color, double Base, double Height) : Shape(Color);

public class AdvancedPatternMatching
{
    public static double CalculateArea(Shape shape)
    {
        return shape switch
        {
            // Property pattern with nested patterns
            Circle { Radius: var r } => Math.PI * r * r,
            
            Rectangle { Width: var w, Height: var h } => w * h,
            
            Triangle { Base: var b, Height: var h } => 0.5 * b * h,
            
            // Pattern with when clause
            Circle { Radius: > 10 } largeCircle => 
                Math.PI * largeCircle.Radius * largeCircle.Radius,
                
            // Recursive pattern
            Circle c => Math.PI * c.Radius * c.Radius,
            
            _ => throw new ArgumentException("Unknown shape")
        };
    }
    
    public static string DescribeShape(Shape shape)
    {
        return shape switch
        {
            Circle c => $"Circle with radius {c.Radius}",
            Rectangle r when r.Width == r.Height => $"Square with side {r.Width}",
            Rectangle r => $"Rectangle {r.Width}x{r.Height}",
            Triangle t => $"Triangle with base {t.Base} and height {t.Height}",
            _ => "Unknown shape"
        };
    }
    
    public static void PatternWithSwitchExpression()
    {
        var shapes = new Shape[]
        {
            new Circle("Red", 5.0),
            new Rectangle("Blue", 4.0, 6.0),
            new Rectangle("Green", 5.0, 5.0),
            new Triangle("Yellow", 3.0, 4.0)
        };
        
        foreach (var shape in shapes)
        {
            var area = CalculateArea(shape);
            var description = DescribeShape(shape);
            Console.WriteLine($"{description}, Area: {area:F2}");
        }
    }
}

Relational Patterns (C# 9+):

csharp

public class RelationalPatternsDemo
{
    public static string CategorizeTemperature(double temp)
    {
        return temp switch
        {
            < -10 => "Extreme Cold",
            >= -10 and < 0 => "Freezing",
            >= 0 and < 15 => "Cold",
            >= 15 and < 25 => "Moderate",
            >= 25 and < 35 => "Warm",
            >= 35 => "Hot"
        };
    }
    
    public static string GradeStudent(int score)
    {
        return score switch
        {
            >= 90 => "A",
            >= 80 => "B",
            >= 70 => "C",
            >= 60 => "D",
            _ => "F"
        };
    }
}

3. Attributes

Attributes add metadata to your code that can be read at runtime via reflection.

Detailed Examples:

Custom Attributes:

csharp

// Custom attribute for validation
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, 
                AllowMultiple = false, Inherited = true)]
public class RequiredAttribute : Attribute
{
    public string ErrorMessage { get; }
    
    public RequiredAttribute(string errorMessage = "This field is required")
    {
        ErrorMessage = errorMessage;
    }
}

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class RangeAttribute : Attribute
{
    public double Min { get; }
    public double Max { get; }
    public string ErrorMessage { get; }
    
    public RangeAttribute(double min, double max, string errorMessage = null)
    {
        Min = min;
        Max = max;
        ErrorMessage = errorMessage ?? $"Value must be between {min} and {max}";
    }
}

[AttributeUsage(AttributeTargets.Class)]
public class DisplayNameAttribute : Attribute
{
    public string Name { get; }
    
    public DisplayNameAttribute(string name)
    {
        Name = name;
    }
}

// Using custom attributes
[DisplayName("User Information")]
public class User
{
    [Required("Username is required")]
    public string Username { get; set; }
    
    [Required]
    [Range(18, 120, "Age must be between 18 and 120")]
    public int Age { get; set; }
    
    [Range(0, 100000, "Salary must be reasonable")]
    public decimal Salary { get; set; }
    
    [Obsolete("Use CreatedAt instead")]
    public DateTime CreateDate { get; set; }
    
    public DateTime CreatedAt { get; set; }
}

// Attribute for method timing
[AttributeUsage(AttributeTargets.Method)]
public class TimeExecutionAttribute : Attribute
{
    public string Category { get; }
    
    public TimeExecutionAttribute(string category = "General")
    {
        Category = category;
    }
}

public class ServiceWithAttributes
{
    [TimeExecution("Database")]
    public void ProcessData()
    {
        Thread.Sleep(100); // Simulate work
    }
    
    [TimeExecution("API")]
    [Obsolete("Use NewApiMethod instead")]
    public void OldApiMethod()
    {
        // Old implementation
    }
    
    public void NewApiMethod()
    {
        // New implementation
    }
}

Reading Attributes with Reflection:

csharp

public class AttributeReader
{
    public static void ReadClassAttributes()
    {
        Type userType = typeof(User);
        
        // Read class-level attributes
        var displayNameAttr = userType.GetCustomAttribute<DisplayNameAttribute>();
        if (displayNameAttr != null)
        {
            Console.WriteLine($"Class display name: {displayNameAttr.Name}");
        }
        
        // Read property attributes
        foreach (var prop in userType.GetProperties())
        {
            Console.WriteLine($"\nProperty: {prop.Name}");
            
            var requiredAttr = prop.GetCustomAttribute<RequiredAttribute>();
            if (requiredAttr != null)
            {
                Console.WriteLine($"  Required: {requiredAttr.ErrorMessage}");
            }
            
            var rangeAttr = prop.GetCustomAttribute<RangeAttribute>();
            if (rangeAttr != null)
            {
                Console.WriteLine($"  Range: {rangeAttr.Min}-{rangeAttr.Max}");
                Console.WriteLine($"  Error: {rangeAttr.ErrorMessage}");
            }
            
            var obsoleteAttr = prop.GetCustomAttribute<ObsoleteAttribute>();
            if (obsoleteAttr != null)
            {
                Console.WriteLine($"  Obsolete: {obsoleteAttr.Message}");
            }
        }
    }
    
    public static List<string> Validate(object obj)
    {
        var errors = new List<string>();
        Type type = obj.GetType();
        
        foreach (var prop in type.GetProperties())
        {
            var value = prop.GetValue(obj);
            
            // Check Required attribute
            var requiredAttr = prop.GetCustomAttribute<RequiredAttribute>();
            if (requiredAttr != null)
            {
                if (value == null || (value is string str && string.IsNullOrEmpty(str)))
                {
                    errors.Add(requiredAttr.ErrorMessage);
                }
            }
            
            // Check Range attribute
            var rangeAttr = prop.GetCustomAttribute<RangeAttribute>();
            if (rangeAttr != null && value is IConvertible convertible)
            {
                double numericValue = convertible.ToDouble(System.Globalization.CultureInfo.InvariantCulture);
                if (numericValue < rangeAttr.Min || numericValue > rangeAttr.Max)
                {
                    errors.Add(rangeAttr.ErrorMessage);
                }
            }
        }
        
        return errors;
    }
}

// Usage
public class AttributeDemo
{
    public static void DemonstrateAttributes()
    {
        // Read attributes
        AttributeReader.ReadClassAttributes();
        
        // Validation using attributes
        var user = new User 
        { 
            Username = "", // Invalid - empty
            Age = 15,      // Invalid - under 18
            Salary = 50000 // Valid
        };
        
        var errors = AttributeReader.Validate(user);
        Console.WriteLine("\nValidation errors:");
        foreach (var error in errors)
        {
            Console.WriteLine($"  - {error}");
        }
    }
}

4. Reflection

Reflection allows you to inspect and interact with types, methods, and properties at runtime.

Detailed Examples:

Basic Reflection:

csharp

public class ReflectionDemo
{
    public static void InspectType(Type type)
    {
        Console.WriteLine($"Type: {type.FullName}");
        Console.WriteLine($"Assembly: {type.Assembly.GetName().Name}");
        Console.WriteLine($"Is Class: {type.IsClass}");
        Console.WriteLine($"Is Public: {type.IsPublic}");
        
        // Properties
        Console.WriteLine("\nProperties:");
        foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            Console.WriteLine($"  {prop.PropertyType.Name} {prop.Name} " +
                            $"(CanRead: {prop.CanRead}, CanWrite: {prop.CanWrite})");
        }
        
        // Methods
        Console.WriteLine("\nMethods:");
        foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
        {
            if (!method.IsSpecialName) // Exclude property getters/setters
            {
                Console.WriteLine($"  {method.ReturnType.Name} {method.Name}()");
            }
        }
        
        // Constructors
        Console.WriteLine("\nConstructors:");
        foreach (var ctor in type.GetConstructors(BindingFlags.Public | BindingFlags.Instance))
        {
            var parameters = ctor.GetParameters();
            var paramString = string.Join(", ", parameters.Select(p => $"{p.ParameterType.Name} {p.Name}"));
            Console.WriteLine($"  {type.Name}({paramString})");
        }
    }
    
    public static void CreateInstanceDynamically()
    {
        Type listType = typeof(List<>);
        Type stringListType = listType.MakeGenericType(typeof(string));
        
        // Create instance of List<string>
        object stringList = Activator.CreateInstance(stringListType);
        
        // Get Add method
        MethodInfo addMethod = stringListType.GetMethod("Add");
        
        // Add items dynamically
        addMethod.Invoke(stringList, new object[] { "Hello" });
        addMethod.Invoke(stringList, new object[] { "World" });
        
        // Get Count property
        PropertyInfo countProp = stringListType.GetProperty("Count");
        int count = (int)countProp.GetValue(stringList);
        
        Console.WriteLine($"Dynamically created list has {count} items: {stringList}");
    }
}

Advanced Reflection:

csharp

public class AdvancedReflection
{
    public static void MethodInvocationDemo()
    {
        var calculator = new Calculator();
        Type calculatorType = calculator.GetType();
        
        // Invoke method by name
        MethodInfo addMethod = calculatorType.GetMethod("Add");
        object result = addMethod.Invoke(calculator, new object[] { 5, 3 });
        Console.WriteLine($"5 + 3 = {result}");
        
        // Invoke generic method
        MethodInfo genericMethod = calculatorType.GetMethod("CreateList");
        MethodInfo constructedMethod = genericMethod.MakeGenericMethod(typeof(string));
        object stringList = constructedMethod.Invoke(calculator, new object[] { 3 });
        
        Console.WriteLine($"Created list: {stringList}");
    }
    
    public static void PropertyManipulation()
    {
        var person = new Person("John", "Doe", 30);
        Type personType = typeof(Person);
        
        // Get property value
        PropertyInfo firstNameProp = personType.GetProperty("FirstName");
        string firstName = (string)firstNameProp.GetValue(person);
        Console.WriteLine($"First Name: {firstName}");
        
        // Set property value (if not readonly)
        PropertyInfo ageProp = personType.GetProperty("Age");
        ageProp.SetValue(person, 35);
        Console.WriteLine($"Updated Age: {person.Age}");
        
        // Work with private fields (use with caution!)
        FieldInfo[] privateFields = personType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
        Console.WriteLine("Private fields:");
        foreach (var field in privateFields)
        {
            Console.WriteLine($"  {field.FieldType.Name} {field.Name}");
        }
    }
    
    public static T CreateGenericType<T>() where T : new()
    {
        Type type = typeof(T);
        Console.WriteLine($"Creating instance of {type.Name}");
        
        // Inspect generic type parameters
        if (type.IsGenericType)
        {
            Type[] genericArgs = type.GetGenericArguments();
            Console.WriteLine("Generic arguments:");
            foreach (var arg in genericArgs)
            {
                Console.WriteLine($"  - {arg.Name}");
            }
        }
        
        return new T();
    }
}

// Supporting classes
public class Calculator
{
    public int Add(int a, int b) => a + b;
    public double Add(double a, double b) => a + b;
    
    public List<T> CreateList<T>(int capacity) => new List<T>(capacity);
    
    private string PrivateMethod() => "This is private";
}

// Usage
public class ReflectionUsage
{
    public static void DemonstrateReflection()
    {
        // Inspect types
        ReflectionDemo.InspectType(typeof(Person));
        ReflectionDemo.InspectType(typeof(List<>));
        
        // Dynamic creation
        ReflectionDemo.CreateInstanceDynamically();
        
        // Method invocation
        AdvancedReflection.MethodInvocationDemo();
        
        // Property manipulation
        AdvancedReflection.PropertyManipulation();
        
        // Generic type creation
        var list = AdvancedReflection.CreateGenericType<List<string>>();
    }
}

5. Dynamic Types

The dynamic keyword bypasses compile-time type checking and resolves types at runtime.

Detailed Examples:

Basic Dynamic Usage:

csharp

public class DynamicDemo
{
    public static void BasicDynamicOperations()
    {
        // Dynamic variables
        dynamic value = 10;
        Console.WriteLine($"Type: {value.GetType()}, Value: {value}");
        
        value = "Hello Dynamic World";
        Console.WriteLine($"Type: {value.GetType()}, Value: {value}");
        
        value = new List<int> { 1, 2, 3 };
        Console.WriteLine($"Type: {value.GetType()}, Count: {value.Count}");
        
        // Dynamic method calls
        dynamic calculator = new Calculator();
        int result = calculator.Add(5, 3); // Resolved at runtime
        Console.WriteLine($"5 + 3 = {result}");
        
        // This would throw RuntimeBinderException at runtime:
        // calculator.NonExistentMethod(); // Commented out to avoid exception
    }
    
    public static void DynamicWithReflection()
    {
        // Using dynamic to simplify reflection
        Type stringType = typeof(string);
        dynamic stringInstance = Activator.CreateInstance(stringType, 'A', 5);
        Console.WriteLine($"Created string: '{stringInstance}'");
        
        // Compare with pure reflection (more verbose):
        object stringObj = Activator.CreateInstance(typeof(string), new object[] { 'A', 5 });
        string result = (string)stringObj; // Need explicit cast
        Console.WriteLine($"With reflection: '{result}'");
    }
    
    public static void ExpandoObjectDemo()
    {
        // ExpandoObject - add properties/methods dynamically
        dynamic person = new ExpandoObject();
        
        // Add properties
        person.Name = "John Doe";
        person.Age = 30;
        person.CreatedAt = DateTime.Now;
        
        // Add method
        person.GetInfo = new Func<string>(() => $"{person.Name} is {person.Age} years old");
        
        Console.WriteLine(person.GetInfo());
        
        // Convert to dictionary
        var dict = (IDictionary<string, object>)person;
        Console.WriteLine("\nProperties as dictionary:");
        foreach (var kvp in dict)
        {
            Console.WriteLine($"  {kvp.Key}: {kvp.Value}");
        }
        
        // Add new property via dictionary
        dict["Email"] = "john@example.com";
        Console.WriteLine($"Email: {person.Email}");
    }
}

Advanced Dynamic Scenarios:

csharp

public class DynamicXmlParser
{
    public static dynamic ParseXml(string xml)
    {
        var doc = new System.Xml.XmlDocument();
        doc.LoadXml(xml);
        return ParseNode(doc.DocumentElement);
    }
    
    private static dynamic ParseNode(System.Xml.XmlNode node)
    {
        dynamic result = new ExpandoObject();
        var dict = (IDictionary<string, object>)result;
        
        // Add attributes as properties
        if (node.Attributes != null)
        {
            foreach (System.Xml.XmlAttribute attr in node.Attributes)
            {
                dict[attr.Name] = attr.Value;
            }
        }
        
        // Handle child elements
        foreach (System.Xml.XmlNode child in node.ChildNodes)
        {
            if (child.NodeType == System.Xml.XmlNodeType.Element)
            {
                if (!dict.ContainsKey(child.Name))
                {
                    dict[child.Name] = ParseNode(child);
                }
                else
                {
                    // Handle multiple elements with same name
                    if (dict[child.Name] is List<dynamic> list)
                    {
                        list.Add(ParseNode(child));
                    }
                    else
                    {
                        var existing = dict[child.Name];
                        dict[child.Name] = new List<dynamic> { existing, ParseNode(child) };
                    }
                }
            }
            else if (child.NodeType == System.Xml.XmlNodeType.Text)
            {
                dict["Value"] = child.InnerText;
            }
        }
        
        return result;
    }
}

public class DynamicUsage
{
    public static void DemonstrateDynamicXml()
    {
        string xml = @"
            <person id='123'>
                <name>John Doe</name>
                <age>30</age>
                <address>
                    <street>123 Main St</street>
                    <city>Springfield</city>
                </address>
                <hobbies>
                    <hobby>Reading</hobby>
                    <hobby>Gaming</hobby>
                </hobbies>
            </person>";
        
        dynamic person = DynamicXmlParser.ParseXml(xml);
        
        Console.WriteLine($"ID: {person.id}");
        Console.WriteLine($"Name: {person.name.Value}");
        Console.WriteLine($"Age: {person.age.Value}");
        Console.WriteLine($"Address: {person.address.street.Value}, {person.address.city.Value}");
        
        Console.WriteLine("Hobbies:");
        foreach (var hobby in person.hobbies.hobby)
        {
            Console.WriteLine($"  - {hobby.Value}");
        }
    }
}

6. Extension Methods

Extension methods allow you to "add" methods to existing types without modifying them.

Detailed Examples:

Basic Extension Methods:

csharp

public static class StringExtensions
{
    // Extension method for string
    public static bool IsNullOrEmpty(this string str)
    {
        return string.IsNullOrEmpty(str);
    }
    
    public static string Reverse(this string str)
    {
        if (string.IsNullOrEmpty(str))
            return str;
            
        char[] charArray = str.ToCharArray();
        Array.Reverse(charArray);
        return new string(charArray);
    }
    
    public static string Truncate(this string str, int maxLength)
    {
        if (string.IsNullOrEmpty(str) || str.Length <= maxLength)
            return str;
            
        return str.Substring(0, maxLength) + "...";
    }
    
    public static bool IsPalindrome(this string str)
    {
        if (string.IsNullOrEmpty(str))
            return false;
            
        string clean = new string(str.Where(char.IsLetterOrDigit).ToArray()).ToLower();
        return clean == clean.Reverse();
    }
}

public static class CollectionExtensions
{
    public static string ToDelimitedString<T>(this IEnumerable<T> collection, string delimiter = ", ")
    {
        return string.Join(delimiter, collection);
    }
    
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> collection, Action<T> action)
    {
        foreach (var item in collection)
        {
            action(item);
            yield return item; // Allows chaining
        }
    }
    
    public static bool IsEmpty<T>(this IEnumerable<T> collection)
    {
        return !collection.Any();
    }
    
    public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T> collection) where T : class
    {
        return collection.Where(item => item != null);
    }
}

public static class NumericExtensions
{
    public static bool IsBetween(this int value, int min, int max)
    {
        return value >= min && value <= max;
    }
    
    public static double ToRadians(this double degrees)
    {
        return degrees * Math.PI / 180.0;
    }
    
    public static double ToDegrees(this double radians)
    {
        return radians * 180.0 / Math.PI;
    }
    
    public static string ToFileSizeString(this long bytes)
    {
        string[] sizes = { "B", "KB", "MB", "GB", "TB" };
        int order = 0;
        double len = bytes;
        
        while (len >= 1024 && order < sizes.Length - 1)
        {
            order++;
            len /= 1024;
        }
        
        return $"{len:0.##} {sizes[order]}";
    }
}

Advanced Extension Methods:

csharp

public static class LINQExtensions
{
    public static IEnumerable<TResult> SelectWithIndex<TSource, TResult>(
        this IEnumerable<TSource> source,
        Func<TSource, int, TResult> selector)
    {
        int index = 0;
        foreach (var item in source)
        {
            yield return selector(item, index++);
        }
    }
    
    public static IEnumerable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(
        this IEnumerable<TFirst> first,
        IEnumerable<TSecond> second)
    {
        using var enum1 = first.GetEnumerator();
        using var enum2 = second.GetEnumerator();
        
        while (enum1.MoveNext() && enum2.MoveNext())
        {
            yield return (enum1.Current, enum2.Current);
        }
    }
    
    public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
    {
        return source.OrderBy(selector).First();
    }
    
    public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
    {
        return source.OrderByDescending(selector).First();
    }
}

public static class DateTimeExtensions
{
    public static DateTime StartOfDay(this DateTime date)
    {
        return new DateTime(date.Year, date.Month, date.Day, 0, 0, 0, 0);
    }
    
    public static DateTime EndOfDay(this DateTime date)
    {
        return new DateTime(date.Year, date.Month, date.Day, 23, 59, 59, 999);
    }
    
    public static bool IsWeekend(this DateTime date)
    {
        return date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday;
    }
    
    public static int Age(this DateTime birthDate)
    {
        var today = DateTime.Today;
        int age = today.Year - birthDate.Year;
        if (birthDate.Date > today.AddYears(-age)) age--;
        return age;
    }
}

Using Extension Methods:

csharp

public class ExtensionMethodDemo
{
    public static void DemonstrateExtensions()
    {
        // String extensions
        string text = "Hello World";
        Console.WriteLine($"Original: {text}");
        Console.WriteLine($"Reversed: {text.Reverse()}");
        Console.WriteLine($"Truncated: {text.Truncate(5)}");
        Console.WriteLine($"Is palindrome: {"radar".IsPalindrome()}");
        
        // Collection extensions
        var numbers = new List<int> { 1, 2, 3, 4, 5 };
        Console.WriteLine($"Numbers: {numbers.ToDelimitedString(" | ")}");
        Console.WriteLine($"Is empty: {numbers.IsEmpty()}");
        
        // Chain extension methods
        numbers
            .Where(n => n > 2)
            .ForEach(n => Console.WriteLine($"Processing: {n}"))
            .Select(n => n * 2)
            .ForEach(n => Console.WriteLine($"Doubled: {n}"));
        
        // Numeric extensions
        int value = 42;
        Console.WriteLine($"42 is between 40 and 50: {value.IsBetween(40, 50)}");
        
        long fileSize = 1024 * 1024 * 5 + 123456;
        Console.WriteLine($"File size: {fileSize.ToFileSizeString()}");
        
        // DateTime extensions
        DateTime now = DateTime.Now;
        Console.WriteLine($"Start of day: {now.StartOfDay()}");
        Console.WriteLine($"Is weekend: {now.IsWeekend()}");
        
        DateTime birthDate = new DateTime(1990, 1, 1);
        Console.WriteLine($"Age: {birthDate.Age()}");
    }
}

7. Real-World Complete Example

csharp

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;

namespace AdvancedFeaturesDemo
{
    // Records for domain models
    public record Product(string Name, decimal Price, string Category, int Stock);
    
    public record Order(int Id, string CustomerName, List<Product> Products, DateTime OrderDate)
    {
        public decimal TotalAmount => Products.Sum(p => p.Price);
        public bool IsRecent => (DateTime.Now - OrderDate).TotalDays < 7;
    }
    
    // Custom attributes
    [AttributeUsage(AttributeTargets.Class)]
    public class CacheableAttribute : Attribute
    {
        public int DurationMinutes { get; }
        
        public CacheableAttribute(int durationMinutes = 30)
        {
            DurationMinutes = durationMinutes;
        }
    }
    
    [AttributeUsage(AttributeTargets.Method)]
    public class LogExecutionAttribute : Attribute
    {
        public string Level { get; }
        
        public LogExecutionAttribute(string level = "Information")
        {
            Level = level;
        }
    }
    
    // Service with attributes
    [Cacheable(60)] // Cache for 60 minutes
    public class OrderService
    {
        private readonly List<Order> _orders = new();
        
        [LogExecution("Information")]
        public void AddOrder(Order order)
        {
            _orders.Add(order);
            Console.WriteLine($"Order {order.Id} added for {order.CustomerName}");
        }
        
        [LogExecution("Debug")]
        public IEnumerable<Order> GetRecentOrders()
        {
            return _orders.Where(o => o.IsRecent);
        }
        
        public decimal GetTotalRevenue()
        {
            return _orders.Sum(o => o.TotalAmount);
        }
    }
    
    // Extension methods
    public static class OrderExtensions
    {
        public static IEnumerable<Order> ForCustomer(this IEnumerable<Order> orders, string customerName)
        {
            return orders.Where(o => o.CustomerName.Equals(customerName, StringComparison.OrdinalIgnoreCase));
        }
        
        public static IEnumerable<Order> InCategory(this IEnumerable<Order> orders, string category)
        {
            return orders.Where(o => o.Products.Any(p => p.Category == category));
        }
        
        public static string ToSummaryString(this Order order)
        {
            return $"Order #{order.Id} by {order.CustomerName}: {order.Products.Count} items, Total: ${order.TotalAmount}";
        }
    }
    
    public static class AttributeExtensions
    {
        public static bool IsCacheable(this Type type)
        {
            return type.GetCustomAttribute<CacheableAttribute>() != null;
        }
        
        public static int? GetCacheDuration(this Type type)
        {
            return type.GetCustomAttribute<CacheableAttribute>()?.DurationMinutes;
        }
    }
    
    // Main demo
    public class Program
    {
        public static void Main()
        {
            Console.WriteLine("=== C# Advanced Features Demo ===\n");
            
            // Records demo
            DemonstrateRecords();
            
            // Pattern matching demo
            DemonstratePatternMatching();
            
            // Attributes and reflection demo
            DemonstrateAttributesAndReflection();
            
            // Extension methods demo
            DemonstrateExtensionMethods();
            
            // Dynamic demo
            DemonstrateDynamicFeatures();
        }
        
        static void DemonstrateRecords()
        {
            Console.WriteLine("1. RECORDS DEMO");
            Console.WriteLine("---------------");
            
            var products = new List<Product>
            {
                new Product("Laptop", 999.99m, "Electronics", 10),
                new Product("Mouse", 29.99m, "Electronics", 50),
                new Product("Desk", 199.99m, "Furniture", 15),
                new Product("Chair", 149.99m, "Furniture", 20)
            };
            
            var order = new Order(1, "John Doe", products.Take(2).ToList(), DateTime.Now.AddDays(-1));
            
            Console.WriteLine(order.ToSummaryString());
            Console.WriteLine($"Is recent: {order.IsRecent}");
            Console.WriteLine();
        }
        
        static void DemonstratePatternMatching()
        {
            Console.WriteLine("2. PATTERN MATCHING DEMO");
            Console.WriteLine("------------------------");
            
            object[] items = { 
                new Product("Laptop", 999.99m, "Electronics", 10),
                42,
                "Hello World",
                new List<int> { 1, 2, 3 },
                null
            };
            
            foreach (var item in items)
            {
                var description = item switch
                {
                    Product { Price: > 500 } p => $"Expensive product: {p.Name}",
                    Product p => $"Product: {p.Name}",
                    int i when i > 30 => $"Large number: {i}",
                    int i => $"Small number: {i}",
                    string s when s.Length > 5 => $"Long string: {s}",
                    string s => $"Short string: {s}",
                    ICollection<int> list => $"List with {list.Count} items",
                    null => "Null value",
                    _ => "Unknown item"
                };
                
                Console.WriteLine($"  {description}");
            }
            Console.WriteLine();
        }
        
        static void DemonstrateAttributesAndReflection()
        {
            Console.WriteLine("3. ATTRIBUTES AND REFLECTION DEMO");
            Console.WriteLine("---------------------------------");
            
            var service = new OrderService();
            Type serviceType = typeof(OrderService);
            
            // Check cacheable attribute
            if (serviceType.IsCacheable())
            {
                int? duration = serviceType.GetCacheDuration();
                Console.WriteLine($"Service is cacheable for {duration} minutes");
            }
            
            // Inspect methods with attributes
            foreach (var method in serviceType.GetMethods(BindingFlags.Public | BindingFlags.Instance))
            {
                var logAttr = method.GetCustomAttribute<LogExecutionAttribute>();
                if (logAttr != null)
                {
                    Console.WriteLine($"Method {method.Name} has log level: {logAttr.Level}");
                }
            }
            Console.WriteLine();
        }
        
        static void DemonstrateExtensionMethods()
        {
            Console.WriteLine("4. EXTENSION METHODS DEMO");
            Console.WriteLine("--------------------------");
            
            var orders = new List<Order>
            {
                new Order(1, "John Doe", 
                    new List<Product> { new Product("Laptop", 999.99m, "Electronics", 10) }, 
                    DateTime.Now.AddDays(-1)),
                new Order(2, "Jane Smith", 
                    new List<Product> { 
                        new Product("Mouse", 29.99m, "Electronics", 50),
                        new Product("Desk", 199.99m, "Furniture", 15)
                    }, 
                    DateTime.Now.AddDays(-5)),
                new Order(3, "John Doe", 
                    new List<Product> { new Product("Chair", 149.99m, "Furniture", 20) }, 
                    DateTime.Now.AddDays(-10))
            };
            
            // Use extension methods
            var johnsOrders = orders.ForCustomer("John Doe");
            var electronicsOrders = orders.InCategory("Electronics");
            
            Console.WriteLine("John's orders:");
            foreach (var order in johnsOrders)
            {
                Console.WriteLine($"  {order.ToSummaryString()}");
            }
            
            Console.WriteLine("\nElectronics orders:");
            foreach (var order in electronicsOrders)
            {
                Console.WriteLine($"  {order.ToSummaryString()}");
            }
            Console.WriteLine();
        }
        
        static void DemonstrateDynamicFeatures()
        {
            Console.WriteLine("5. DYNAMIC FEATURES DEMO");
            Console.WriteLine("-------------------------");
            
            // ExpandoObject for dynamic data
            dynamic report = new ExpandoObject();
            report.Title = "Sales Report";
            report.GeneratedAt = DateTime.Now;
            report.TotalRevenue = 15000.50m;
            
            // Add method dynamically
            report.GetSummary = new Func<string>(() => 
                $"{report.Title} generated at {report.GeneratedAt}: ${report.TotalRevenue}");
            
            Console.WriteLine(report.GetSummary());
            
            // Access as dictionary
            var dict = (IDictionary<string, object>)report;
            dict["Author"] = "Reporting System";
            
            Console.WriteLine($"Report author: {report.Author}");
        }
    }
}

Key Advanced Features Summary

  1. Records: Immutable data types with value semantics

  2. Pattern Matching: Concise conditional logic based on shape and content

  3. Attributes: Metadata that can be inspected at runtime

  4. Reflection: Runtime type inspection and dynamic code execution

  5. Dynamic Types: Late binding and dynamic object creation

  6. Extension Methods: Adding methods to existing types without inheritance

These features make C# incredibly powerful for modern application development, enabling more expressive, maintainable, and flexible code.

Comments (0)

No comments yet. Be the first to comment!

LecturreAI Assistant

Hello! I'm LecturreAI

What would you like to learn today?