MODULE 6: ADVANCED C# & .NET TECHNOLOGIES - Complete Guide
61. Reflection (Runtime Type Inspection)
using System;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;
public class ReflectionDemo
{
// BASIC REFLECTION
public void BasicReflection()
{
// Get type from object
var person = new Person("John", 30);
Type type1 = person.GetType();
// Get type from class name
Type type2 = typeof(Person);
// Get type from string (fully qualified name)
Type type3 = Type.GetType("System.String");
Console.WriteLine($"Type name: {type1.Name}");
Console.WriteLine($"Full name: {type1.FullName}");
Console.WriteLine($"Namespace: {type1.Namespace}");
Console.WriteLine($"Assembly: {type1.Assembly.GetName().Name}");
}
// INSPECTING MEMBERS
public void InspectMembers()
{
Type type = typeof(Person);
// Get all public methods
Console.WriteLine("Methods:");
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
{
Console.WriteLine($" {method.ReturnType.Name} {method.Name}({string.Join(", ", method.GetParameters().Select(p => $"{p.ParameterType.Name} {p.Name}"))})");
}
// Get all properties
Console.WriteLine("\nProperties:");
foreach (var prop in type.GetProperties())
{
Console.WriteLine($" {prop.PropertyType.Name} {prop.Name} {{ get; set; }}");
}
// Get all fields
Console.WriteLine("\nFields:");
foreach (var field in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
{
Console.WriteLine($" {field.FieldType.Name} {field.Name}");
}
// Get constructors
Console.WriteLine("\nConstructors:");
foreach (var ctor in type.GetConstructors())
{
var parameters = string.Join(", ", ctor.GetParameters().Select(p => $"{p.ParameterType.Name} {p.Name}"));
Console.WriteLine($" Person({parameters})");
}
// Get interfaces
Console.WriteLine("\nInterfaces:");
foreach (var iface in type.GetInterfaces())
{
Console.WriteLine($" {iface.Name}");
}
}
// INVOKING METHODS DYNAMICALLY
public void DynamicInvocation()
{
Type type = typeof(Person);
// Create instance
object person = Activator.CreateInstance(type, "Alice", 25);
// Get method
MethodInfo method = type.GetMethod("SayHello");
// Invoke method
string result = (string)method.Invoke(person, null);
Console.WriteLine(result);
// Invoke method with parameters
MethodInfo greetMethod = type.GetMethod("Greet");
string greeting = (string)greetMethod.Invoke(person, new object[] { "Bob" });
Console.WriteLine(greeting);
// Call static method
MethodInfo staticMethod = type.GetMethod("GetSpecies");
string species = (string)staticMethod.Invoke(null, null);
Console.WriteLine($"Species: {species}");
}
// GETTING/SETTING PROPERTIES
public void PropertyManipulation()
{
var person = new Person("John", 30);
Type type = person.GetType();
// Get property value
PropertyInfo nameProp = type.GetProperty("Name");
string name = (string)nameProp.GetValue(person);
Console.WriteLine($"Current name: {name}");
// Set property value
nameProp.SetValue(person, "Jonathan");
Console.WriteLine($"New name: {person.Name}");
// Get all property values
foreach (var prop in type.GetProperties())
{
object value = prop.GetValue(person);
Console.WriteLine($"{prop.Name}: {value}");
}
}
// WORKING WITH PRIVATE MEMBERS
public void AccessPrivateMembers()
{
var person = new Person("John", 30);
Type type = person.GetType();
// Access private field
FieldInfo privateField = type.GetField("_secret", BindingFlags.NonPublic | BindingFlags.Instance);
privateField.SetValue(person, "New secret");
string secret = (string)privateField.GetValue(person);
Console.WriteLine($"Private field value: {secret}");
// Call private method
MethodInfo privateMethod = type.GetMethod("PrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);
privateMethod.Invoke(person, null);
}
// CREATING INSTANCES
public void CreateInstances()
{
// Default constructor
Type type = typeof(Person);
object person1 = Activator.CreateInstance(type);
// Parameterized constructor
object person2 = Activator.CreateInstance(type, "John", 30);
// With non-public constructor
object person3 = Activator.CreateInstance(type, nonPublic: true);
// Generic version
Person person4 = Activator.CreateInstance<Person>();
}
// ENUMERATING ASSEMBLY TYPES
public void EnumerateAssemblyTypes()
{
Assembly assembly = Assembly.GetExecutingAssembly();
foreach (Type type in assembly.GetTypes())
{
if (type.IsClass && !type.IsAbstract)
{
Console.WriteLine($"Class: {type.Name}");
// Check for attributes
if (type.GetCustomAttribute<SerializableAttribute>() != null)
{
Console.WriteLine(" [Serializable]");
}
// Check inheritance
if (type.BaseType != null && type.BaseType != typeof(object))
{
Console.WriteLine($" Inherits from: {type.BaseType.Name}");
}
}
}
}
// CUSTOM ATTRIBUTE PROCESSING
public void ProcessCustomAttributes()
{
Type type = typeof(ServiceClass);
// Class attribute
ServiceAttribute serviceAttr = type.GetCustomAttribute<ServiceAttribute>();
if (serviceAttr != null)
{
Console.WriteLine($"Service Name: {serviceAttr.ServiceName}");
Console.WriteLine($"Service Version: {serviceAttr.Version}");
}
// Method attribute
MethodInfo method = type.GetMethod("ProcessData");
OperationAttribute opAttr = method.GetCustomAttribute<OperationAttribute>();
if (opAttr != null)
{
Console.WriteLine($"Operation: {opAttr.OperationName}");
Console.WriteLine($"Requires Auth: {opAttr.RequiresAuthorization}");
}
// Property attribute
PropertyInfo prop = type.GetProperty("Id");
RequiredAttribute requiredAttr = prop.GetCustomAttribute<RequiredAttribute>();
if (requiredAttr != null)
{
Console.WriteLine("Id property is required");
}
}
}
// CUSTOM ATTRIBUTES
[AttributeUsage(AttributeTargets.Class)]
public class ServiceAttribute : Attribute
{
public string ServiceName { get; set; }
public string Version { get; set; }
public ServiceAttribute(string serviceName)
{
ServiceName = serviceName;
Version = "1.0";
}
}
[AttributeUsage(AttributeTargets.Method)]
public class OperationAttribute : Attribute
{
public string OperationName { get; set; }
public bool RequiresAuthorization { get; set; }
}
[AttributeUsage(AttributeTargets.Property)]
public class RequiredAttribute : Attribute { }
// SAMPLE CLASSES
[Service("PersonService", Version = "2.0")]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
private string _secret = "Default secret";
public Person() : this("Unknown", 0) { }
public Person(string name, int age)
{
Name = name;
Age = age;
}
private Person(string name) => Name = name;
public string SayHello() => $"Hello, I'm {Name}";
public string Greet(string otherName) => $"{Name} says hello to {otherName}";
public static string GetSpecies() => "Homo Sapiens";
private void PrivateMethod() => Console.WriteLine("Private method called");
}
[Service("DataService", Version = "1.0")]
public class ServiceClass
{
[Required]
public int Id { get; set; }
[Operation(OperationName = "Process", RequiresAuthorization = true)]
public void ProcessData() { }
}
62. Attributes (Metadata)
using System;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using System.Runtime.Serialization;
public class AttributesDemo
{
// DEFINING CUSTOM ATTRIBUTES
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property,
AllowMultiple = true,
Inherited = true)]
public class AuthorAttribute : Attribute
{
public string Name { get; set; }
public string Email { get; set; }
public DateTime Date { get; set; }
public AuthorAttribute(string name)
{
Name = name;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DescriptionAttribute : Attribute
{
public string Text { get; set; }
public DescriptionAttribute(string text) => Text = text;
}
// USING ATTRIBUTES
[Author("John Doe", Email = "john@example.com", Date = "2024-01-01")]
[Description("Main application class")]
public class Application
{
[Author("Jane Smith", Email = "jane@example.com")]
[Description("Starts the application")]
public void Start()
{
Console.WriteLine("Application started");
}
[Obsolete("Use ProcessDataAsync instead")]
public void ProcessData() { }
[Obsolete("This method is deprecated", true)] // Will cause compilation error
public void OldMethod() { }
}
// VALIDATION ATTRIBUTES
public class UserRegistration
{
[Required]
[StringLength(50, MinimumLength = 3)]
public string UserName { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[Range(18, 120)]
public int Age { get; set; }
[Phone]
public string PhoneNumber { get; set; }
[RegularExpression(@"^[A-Z]+$", ErrorMessage = "Only uppercase letters allowed")]
public string Code { get; set; }
[DataType(DataType.Password)]
public string Password { get; set; }
[Compare("Password")]
public string ConfirmPassword { get; set; }
}
// SERIALIZATION ATTRIBUTES
[Serializable]
[DataContract]
public class DataObject
{
[DataMember(Name = "id")]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[IgnoreDataMember]
public string InternalData { get; set; }
[NonSerialized]
private string _tempData;
[OnSerializing]
internal void OnSerializing(StreamingContext context)
{
Console.WriteLine("Serializing...");
}
[OnDeserialized]
internal void OnDeserialized(StreamingContext context)
{
Console.WriteLine("Deserialized...");
}
}
// CUSTOM VALIDATION ATTRIBUTE
public class ValidCountryAttribute : ValidationAttribute
{
private static readonly string[] ValidCountries = { "USA", "Canada", "UK", "Australia" };
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null && ValidCountries.Contains(value.ToString()))
{
return ValidationResult.Success;
}
return new ValidationResult($"Invalid country. Valid countries: {string.Join(", ", ValidCountries)}");
}
}
public class Address
{
[ValidCountry]
public string Country { get; set; }
}
// ATTRIBUTE TARGETS
[AttributeUsage(AttributeTargets.All)]
public class AllTargetsAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class TypeTargetsAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor)]
public class MethodTargetsAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class MemberTargetsAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue)]
public class ParameterTargetsAttribute : Attribute { }
// PROCESSING ATTRIBUTES AT RUNTIME
public class AttributeProcessor
{
public static void Validate(object obj)
{
var type = obj.GetType();
var properties = type.GetProperties();
foreach (var prop in properties)
{
var validationAttrs = prop.GetCustomAttributes<ValidationAttribute>();
foreach (var attr in validationAttrs)
{
var value = prop.GetValue(obj);
var context = new ValidationContext(obj, null, null);
attr.Validate(value, context);
}
}
}
public static string GetDescription(MemberInfo member)
{
var descAttr = member.GetCustomAttribute<DescriptionAttribute>();
return descAttr?.Text ?? "No description";
}
public static List<AuthorAttribute> GetAuthors(MemberInfo member)
{
return member.GetCustomAttributes<AuthorAttribute>().ToList();
}
}
// REAL-WORLD: ROUTING ATTRIBUTES (like ASP.NET Core)
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class RouteAttribute : Attribute
{
public string Template { get; }
public RouteAttribute(string template) => Template = template;
}
[AttributeUsage(AttributeTargets.Method)]
public class HttpMethodAttribute : Attribute
{
public string Method { get; }
public HttpMethodAttribute(string method) => Method = method;
}
public class GetAttribute : HttpMethodAttribute
{
public GetAttribute() : base("GET") { }
public GetAttribute(string route) : base("GET") { }
}
public class PostAttribute : HttpMethodAttribute
{
public PostAttribute() : base("POST") { }
}
// API CONTROLLER EXAMPLE
[Route("api/users")]
public class UsersController
{
[Get]
[Route("{id}")]
public User GetUser(int id) => new User();
[Post]
public void CreateUser([FromBody] User user) { }
[HttpGet]
public List<User> GetAllUsers() => new List<User>();
}
public class FromBodyAttribute : Attribute { }
public class HttpGetAttribute : HttpMethodAttribute
{
public HttpGetAttribute() : base("GET") { }
}
public class User { }
}
// BUILDING AN ATTRIBUTE-BASED VALIDATION FRAMEWORK
public class ValidationFramework
{
public class ValidationResult
{
public bool IsValid { get; set; }
public List<string> Errors { get; set; } = new List<string>();
}
public static ValidationResult Validate(object obj)
{
var result = new ValidationResult { IsValid = true };
var type = obj.GetType();
foreach (var prop in type.GetProperties())
{
var value = prop.GetValue(obj);
foreach (var attr in prop.GetCustomAttributes<ValidationAttribute>())
{
try
{
attr.Validate(value, new ValidationContext(obj));
}
catch (ValidationException ex)
{
result.IsValid = false;
result.Errors.Add($"{prop.Name}: {ex.Message}");
}
}
}
return result;
}
}
// USAGE
public class AttributeUsageExample
{
public void Demo()
{
var user = new UserRegistration
{
UserName = "Jo",
Email = "invalid-email",
Age = 15
};
var result = ValidationFramework.Validate(user);
if (!result.IsValid)
{
Console.WriteLine("Validation failed:");
foreach (var error in result.Errors)
{
Console.WriteLine($" {error}");
}
}
// Reading attributes
var type = typeof(Application);
var description = AttributeProcessor.GetDescription(type);
var authors = AttributeProcessor.GetAuthors(type);
}
}
63. Dynamic Programming (dynamic Keyword)
using System;
using System.Dynamic;
using System.Collections.Generic;
public class DynamicProgrammingDemo
{
// BASIC DYNAMIC USAGE
public void BasicDynamic()
{
// dynamic bypasses compile-time type checking
dynamic value = 10;
Console.WriteLine(value.GetType()); // System.Int32
value = "Hello";
Console.WriteLine(value.GetType()); // System.String
Console.WriteLine(value.Length); // 5 (works at runtime)
value = new Person("John", 30);
Console.WriteLine(value.Name); // "John" (works if property exists)
// This compiles but throws at runtime
try
{
Console.WriteLine(value.NonExistentProperty); // RuntimeBinderException
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
// DYNAMIC WITH COM OBJECTS (Office Interop)
public void ComInteropDynamic()
{
// Without dynamic (painful)
// var excel = new Microsoft.Office.Interop.Excel.Application();
// excel.Workbooks.Add();
// ((Microsoft.Office.Interop.Excel._Worksheet)excel.ActiveSheet).Range["A1"].Value = "Hello";
// With dynamic (clean)
/*
dynamic excel = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
excel.Visible = true;
excel.Workbooks.Add();
excel.ActiveSheet.Range["A1"].Value = "Hello World";
*/
Console.WriteLine("Dynamic makes COM interop much cleaner");
}
// DYNAMIC VS VAR VS OBJECT
public void Comparison()
{
// var - compile-time type inference
var v = 10; // Type is int at compile-time
// v = "Hello"; // Error! Can't change type
// object - boxing, needs casting
object obj = 10;
obj = "Hello"; // Works (reassignment)
string str = (string)obj; // Need explicit cast
// dynamic - runtime resolution
dynamic dyn = 10;
dyn = "Hello"; // Works
string str2 = dyn; // No cast needed (but could fail at runtime)
}
// DYNAMIC OBJECTS (ExpandoObject)
public void ExpandoObjectDemo()
{
// Create dynamic object
dynamic person = new ExpandoObject();
// Add properties dynamically
person.Name = "John Doe";
person.Age = 30;
person.City = "New York";
// Add method
person.SayHello = (Action)(() => Console.WriteLine($"Hello, I'm {person.Name}"));
// Add property that's a function
person.GetDescription = (Func<string>)(() => $"{person.Name}, {person.Age} years old");
// Use dynamic object
Console.WriteLine($"Name: {person.Name}");
Console.WriteLine($"Age: {person.Age}");
Console.WriteLine($"City: {person.City}");
person.SayHello();
Console.WriteLine(person.GetDescription());
// Iterate over properties (ExpandoObject implements IDictionary)
var dict = (IDictionary<string, object>)person;
foreach (var kvp in dict)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
}
// CUSTOM DYNAMIC OBJECT
public class DynamicConfiguration : DynamicObject
{
private readonly Dictionary<string, object> _properties = new Dictionary<string, object>();
private readonly Dictionary<string, Func<object[], object>> _methods = new Dictionary<string, Func<object[], object>>();
// Get property
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
string name = binder.Name;
if (_properties.ContainsKey(name))
{
result = _properties[name];
return true;
}
result = null;
return false;
}
// Set property
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_properties[binder.Name] = value;
return true;
}
// Invoke method
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (_methods.ContainsKey(binder.Name))
{
result = _methods[binder.Name](args);
return true;
}
result = null;
return false;
}
// Add method dynamically
public void AddMethod(string name, Func<object[], object> method)
{
_methods[name] = method;
}
// Get property as string
public override bool TryConvert(ConvertBinder binder, out object result)
{
if (binder.Type == typeof(string))
{
result = _properties.ToString();
return true;
}
result = null;
return false;
}
}
public void CustomDynamicObjectDemo()
{
dynamic config = new DynamicConfiguration();
// Set properties
config.ServerName = "localhost";
config.Port = 8080;
config.UseSsl = true;
// Add method
config.AddMethod("GetConnectionString", args =>
{
string server = config.ServerName;
int port = config.Port;
bool useSsl = config.UseSsl;
return $"Server={server}:{port};Ssl={useSsl}";
});
// Use dynamic object
Console.WriteLine($"Server: {config.ServerName}");
Console.WriteLine($"Port: {config.Port}");
Console.WriteLine($"Connection string: {config.GetConnectionString()}");
}
// DYNAMIC IN JSON (Newtonsoft.Json)
public void DynamicJsonDemo()
{
string json = @"
{
'name': 'John Doe',
'age': 30,
'address': {
'city': 'New York',
'zip': 10001
},
'hobbies': ['reading', 'swimming']
}";
// Parse JSON dynamically
// dynamic data = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
// Console.WriteLine(data.name);
// Console.WriteLine(data.address.city);
// Console.WriteLine(string.Join(", ", data.hobbies));
// Create JSON dynamically
/*
dynamic obj = new ExpandoObject();
obj.Name = "Jane";
obj.Age = 25;
obj.Address = new ExpandoObject();
obj.Address.City = "Boston";
string newJson = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
*/
}
// DYNAMIC WITH DLANGUAGE
public class DynamicCalculator : DynamicObject
{
private Dictionary<string, Func<double, double, double>> _operations =
new Dictionary<string, Func<double, double, double>>
{
["add"] = (a, b) => a + b,
["subtract"] = (a, b) => a - b,
["multiply"] = (a, b) => a * b,
["divide"] = (a, b) => a / b
};
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
{
if (args.Length == 3 && args[0] is string op && args[1] is double a && args[2] is double b)
{
if (_operations.ContainsKey(op))
{
result = _operations[op](a, b);
return true;
}
}
result = null;
return false;
}
public dynamic Add(double a, double b) => _operations["add"](a, b);
public dynamic Subtract(double a, double b) => _operations["subtract"](a, b);
}
// REAL-WORLD: DYNAMIC RULE ENGINE
public class DynamicRuleEngine
{
private readonly List<dynamic> _rules = new List<dynamic>();
public void AddRule(dynamic rule)
{
_rules.Add(rule);
}
public bool Evaluate(dynamic context)
{
foreach (var rule in _rules)
{
try
{
if (rule.Condition(context))
{
rule.Action(context);
return true;
}
}
catch (Exception ex)
{
Console.WriteLine($"Rule evaluation error: {ex.Message}");
}
}
return false;
}
}
// PERFORMANCE CONSIDERATIONS
public void DynamicPerformance()
{
// Dynamic has overhead (runtime binding, cache misses)
// Use sparingly in performance-critical code
const int iterations = 1_000_000;
// Static binding (fast)
var staticObj = new Person("John", 30);
var sw = System.Diagnostics.Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
string name = staticObj.Name;
}
sw.Stop();
Console.WriteLine($"Static: {sw.ElapsedMilliseconds}ms");
// Dynamic binding (slower)
dynamic dynamicObj = new Person("John", 30);
sw.Restart();
for (int i = 0; i < iterations; i++)
{
string name = dynamicObj.Name;
}
sw.Stop();
Console.WriteLine($"Dynamic: {sw.ElapsedMilliseconds}ms");
}
}
// WHEN TO USE DYNAMIC
// ✅ Good scenarios:
// - COM interop (Office, IE, etc.)
// - Working with dynamic languages (IronPython, IronRuby)
// - JSON parsing without types
// - Reflection replacement (simpler syntax)
// - Dynamic rule engines
// - View models in ASP.NET MVC (ViewBag)
// ❌ Bad scenarios:
// - Performance-critical code
// - Type-safe operations
// - APIs with known types
// - Compile-time error checking needed
64. Dependency Injection (DI)
using System;
using System.Collections.Generic;
using System.Linq;
public class DependencyInjectionDemo
{
// DEPENDENCY INVERSION PRINCIPLE
// High-level modules should not depend on low-level modules
// Both should depend on abstractions (interfaces)
// BAD DESIGN (Tight coupling)
public class BadEmailService
{
public void SendEmail(string to, string message)
{
Console.WriteLine($"Sending email to {to}: {message}");
}
}
public class BadNotificationService
{
private BadEmailService _emailService = new BadEmailService(); // Hard dependency
public void Notify(string user, string message)
{
_emailService.SendEmail(user, message);
}
}
// GOOD DESIGN (Dependency Injection)
public interface IEmailService
{
void SendEmail(string to, string message);
}
public interface ILogger
{
void Log(string message);
}
public class SmtpEmailService : IEmailService
{
private readonly string _smtpServer;
private readonly ILogger _logger;
public SmtpEmailService(string smtpServer, ILogger logger)
{
_smtpServer = smtpServer;
_logger = logger;
}
public void SendEmail(string to, string message)
{
_logger.Log($"Sending via {_smtpServer} to {to}");
Console.WriteLine($"Email sent to {to}: {message}");
}
}
public class ConsoleLogger : ILogger
{
public void Log(string message) => Console.WriteLine($"[LOG] {DateTime.Now}: {message}");
}
public class NotificationService
{
private readonly IEmailService _emailService;
private readonly ILogger _logger;
// Constructor Injection (most common)
public NotificationService(IEmailService emailService, ILogger logger)
{
_emailService = emailService ?? throw new ArgumentNullException(nameof(emailService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public void Notify(string user, string message)
{
_logger.Log($"Notifying {user}");
_emailService.SendEmail(user, message);
}
}
// SIMPLE DI CONTAINER
public class SimpleContainer
{
private readonly Dictionary<Type, Func<object>> _registrations = new Dictionary<Type, Func<object>>();
public void Register<TService, TImplementation>() where TImplementation : TService
{
_registrations[typeof(TService)] = () => Resolve(typeof(TImplementation));
}
public void RegisterSingleton<TService>(TService instance)
{
_registrations[typeof(TService)] = () => instance;
}
public void Register<TService>(Func<TService> factory)
{
_registrations[typeof(TService)] = () => factory();
}
public TService Resolve<TService>()
{
return (TService)Resolve(typeof(TService));
}
private object Resolve(Type type)
{
if (_registrations.TryGetValue(type, out var factory))
{
return factory();
}
// Try to create with constructor injection
var constructors = type.GetConstructors();
if (constructors.Length == 0)
throw new InvalidOperationException($"No public constructor for {type.Name}");
var constructor = constructors[0];
var parameters = constructor.GetParameters();
var arguments = parameters.Select(p => Resolve(p.ParameterType)).ToArray();
return constructor.Invoke(arguments);
}
}
// USING THE SIMPLE CONTAINER
public void ContainerDemo()
{
var container = new SimpleContainer();
// Register dependencies
container.Register<ILogger, ConsoleLogger>();
container.Register<IEmailService>(() => new SmtpEmailService("smtp.gmail.com", container.Resolve<ILogger>()));
container.Register<NotificationService, NotificationService>();
// Resolve
var notificationService = container.Resolve<NotificationService>();
notificationService.Notify("user@example.com", "Hello from DI!");
}
// DI PATTERNS
public class PatternsDemo
{
// 1. Constructor Injection (Most common)
public class ServiceWithCtorInjection
{
private readonly IDependency _dependency;
public ServiceWithCtorInjection(IDependency dependency)
{
_dependency = dependency;
}
}
// 2. Property Injection (optional dependencies)
public class ServiceWithPropertyInjection
{
public IOptionalDependency OptionalService { get; set; }
public void DoWork()
{
OptionalService?.DoOptionalWork();
}
}
// 3. Method Injection (for occasional dependencies)
public class ServiceWithMethodInjection
{
public void DoWork(IDependency dependency)
{
dependency.DoSomething();
}
}
// 4. Factory Pattern (for runtime dependencies)
public interface IDependencyFactory
{
IDependency Create(string runtimeData);
}
public class ServiceWithFactory
{
private readonly IDependencyFactory _factory;
public ServiceWithFactory(IDependencyFactory factory)
{
_factory = factory;
}
public void DoWork(string runtimeData)
{
var dependency = _factory.Create(runtimeData);
dependency.DoSomething();
}
}
}
// LIFETIME MANAGEMENT
public class LifetimeDemo
{
// Transient: New instance every time
public interface ITransientService { }
public class TransientService : ITransientService { }
// Scoped: One instance per scope (e.g., HTTP request)
public interface IScopedService { }
public class ScopedService : IScopedService { }
// Singleton: One instance for entire application
public interface ISingletonService { }
public class SingletonService : ISingletonService { }
// Manual implementation
public class LifetimeContainer
{
private readonly Dictionary<Type, object> _singletons = new();
private readonly Dictionary<Type, Func<object>> _factories = new();
public void RegisterSingleton<T>(Func<T> factory)
{
_factories[typeof(T)] = () =>
{
lock (_singletons)
{
if (!_singletons.ContainsKey(typeof(T)))
_singletons[typeof(T)] = factory();
return _singletons[typeof(T)];
}
};
}
public void RegisterTransient<T>(Func<T> factory)
{
_factories[typeof(T)] = () => factory();
}
public T Resolve<T>() => (T)_factories[typeof(T)]();
}
}
// REAL-WORLD: ASP.NET Core DI Simulation
public interface IRepository<T>
{
T GetById(int id);
void Add(T entity);
}
public class UserRepository : IRepository<User>
{
private readonly ILogger _logger;
private static List<User> _users = new();
public UserRepository(ILogger logger)
{
_logger = logger;
}
public User GetById(int id) => _users.FirstOrDefault(u => u.Id == id);
public void Add(User entity)
{
_logger.Log($"Adding user: {entity.Name}");
_users.Add(entity);
}
}
public interface IUserService
{
User GetUser(int id);
void CreateUser(string name, string email);
}
public class UserService : IUserService
{
private readonly IRepository<User> _userRepository;
private readonly IEmailService _emailService;
private readonly ILogger _logger;
public UserService(IRepository<User> userRepository, IEmailService emailService, ILogger logger)
{
_userRepository = userRepository;
_emailService = emailService;
_logger = logger;
}
public User GetUser(int id)
{
_logger.Log($"Getting user {id}");
return _userRepository.GetById(id);
}
public void CreateUser(string name, string email)
{
var user = new User { Id = new Random().Next(), Name = name, Email = email };
_userRepository.Add(user);
_emailService.SendEmail(email, $"Welcome {name}!");
_logger.Log($"User created: {name}");
}
}
// DI CONTAINER CONFIGURATION (like Startup.cs in ASP.NET Core)
public class Startup
{
public IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
// Register services
services.AddSingleton<ILogger, ConsoleLogger>();
services.AddTransient<IEmailService, SmtpEmailService>(sp =>
new SmtpEmailService("smtp.gmail.com", sp.GetService<ILogger>()));
services.AddScoped<IRepository<User>, UserRepository>();
services.AddTransient<IUserService, UserService>();
return services.BuildServiceProvider();
}
}
public class ServiceCollection
{
private readonly Dictionary<Type, ServiceDescriptor> _services = new();
public void AddSingleton<TService, TImplementation>() where TImplementation : TService
{
_services[typeof(TService)] = new ServiceDescriptor(typeof(TImplementation), Lifetime.Singleton);
}
public void AddTransient<TService>(Func<IServiceProvider, TService> factory) where TService : class
{
_services[typeof(TService)] = new ServiceDescriptor(factory, Lifetime.Transient);
}
public void AddScoped<TService, TImplementation>() where TImplementation : TService
{
_services[typeof(TService)] = new ServiceDescriptor(typeof(TImplementation), Lifetime.Scoped);
}
public IServiceProvider BuildServiceProvider()
{
return new ServiceProvider(_services);
}
}
public class ServiceProvider : IServiceProvider
{
private readonly Dictionary<Type, ServiceDescriptor> _services;
private readonly Dictionary<Type, object> _singletons = new();
private readonly Dictionary<Type, object> _scopedInstances = new();
public ServiceProvider(Dictionary<Type, ServiceDescriptor> services)
{
_services = services;
}
public object GetService(Type serviceType)
{
if (!_services.TryGetValue(serviceType, out var descriptor))
return null;
switch (descriptor.Lifetime)
{
case Lifetime.Singleton:
if (!_singletons.ContainsKey(serviceType))
_singletons[serviceType] = CreateInstance(descriptor);
return _singletons[serviceType];
case Lifetime.Scoped:
if (!_scopedInstances.ContainsKey(serviceType))
_scopedInstances[serviceType] = CreateInstance(descriptor);
return _scopedInstances[serviceType];
case Lifetime.Transient:
return CreateInstance(descriptor);
default:
return null;
}
}
private object CreateInstance(ServiceDescriptor descriptor)
{
if (descriptor.Factory != null)
return descriptor.Factory(this);
// Create instance with constructor injection
var type = descriptor.ImplementationType;
var constructors = type.GetConstructors();
var parameters = constructors[0].GetParameters();
var arguments = parameters.Select(p => GetService(p.ParameterType)).ToArray();
return Activator.CreateInstance(type, arguments);
}
}
public class ServiceDescriptor
{
public Type ImplementationType { get; }
public Lifetime Lifetime { get; }
public Func<IServiceProvider, object> Factory { get; }
public ServiceDescriptor(Type type, Lifetime lifetime)
{
ImplementationType = type;
Lifetime = lifetime;
}
public ServiceDescriptor(Func<IServiceProvider, object> factory, Lifetime lifetime)
{
Factory = factory;
Lifetime = lifetime;
}
}
public enum Lifetime
{
Transient,
Scoped,
Singleton
}
public interface IServiceProvider
{
object GetService(Type serviceType);
}
public static class ServiceProviderExtensions
{
public static T GetService<T>(this IServiceProvider provider)
{
return (T)provider.GetService(typeof(T));
}
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
// INTERFACES
public interface IDependency
{
void DoSomething();
}
public interface IOptionalDependency
{
void DoOptionalWork();
}
}
// BENEFITS OF DI
// ✅ Loose coupling
// ✅ Testability (easy to mock)
// ✅ Maintainability (swap implementations)
// ✅ Centralized configuration
// ✅ Lifetime management
// ✅ Cleaner code (SRP, DIP)
65. Design Patterns in C# (Practical Implementation)
using System;
using System.Collections.Generic;
using System.Threading;
public class DesignPatternsDemo
{
// 1. SINGLETON PATTERN (Thread-safe)
public class DatabaseConnection
{
private static DatabaseConnection _instance;
private static readonly object _lock = new object();
private static int _instanceCount = 0;
// Private constructor prevents instantiation
private DatabaseConnection()
{
_instanceCount++;
Console.WriteLine($"Database connection created. Instance #{_instanceCount}");
}
// Thread-safe lazy initialization
public static DatabaseConnection Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = new DatabaseConnection();
}
}
}
return _instance;
}
}
public void Connect() => Console.WriteLine("Connected to database");
public void Disconnect() => Console.WriteLine("Disconnected from database");
// Alternative: Lazy<T> (simpler, thread-safe)
private static readonly Lazy<DatabaseConnection> _lazyInstance =
new Lazy<DatabaseConnection>(() => new DatabaseConnection());
public static DatabaseConnection LazyInstance => _lazyInstance.Value;
}
// 2. FACTORY PATTERN
public interface IProduct
{
string GetName();
decimal GetPrice();
}
public class ElectronicProduct : IProduct
{
public string GetName() => "Laptop";
public decimal GetPrice() => 999.99m;
}
public class ClothingProduct : IProduct
{
public string GetName() => "T-Shirt";
public decimal GetPrice() => 29.99m;
}
public interface IProductFactory
{
IProduct CreateProduct();
}
public class ElectronicFactory : IProductFactory
{
public IProduct CreateProduct() => new ElectronicProduct();
}
public class ClothingFactory : IProductFactory
{
public IProduct CreateProduct() => new ClothingProduct();
}
// Abstract Factory Pattern
public interface IGUIFactory
{
IButton CreateButton();
ITextBox CreateTextBox();
}
public interface IButton
{
void Render();
void OnClick();
}
public interface ITextBox
{
void Render();
void SetText(string text);
}
public class WindowsButton : IButton
{
public void Render() => Console.WriteLine("Rendering Windows button");
public void OnClick() => Console.WriteLine("Windows button clicked");
}
public class WindowsTextBox : ITextBox
{
public void Render() => Console.WriteLine("Rendering Windows text box");
public void SetText(string text) => Console.WriteLine($"Windows text box: {text}");
}
public class MacButton : IButton
{
public void Render() => Console.WriteLine("Rendering Mac button");
public void OnClick() => Console.WriteLine("Mac button clicked");
}
public class MacTextBox : ITextBox
{
public void Render() => Console.WriteLine("Rendering Mac text box");
public void SetText(string text) => Console.WriteLine($"Mac text box: {text}");
}
public class WindowsFactory : IGUIFactory
{
public IButton CreateButton() => new WindowsButton();
public ITextBox CreateTextBox() => new WindowsTextBox();
}
public class MacFactory : IGUIFactory
{
public IButton CreateButton() => new MacButton();
public ITextBox CreateTextBox() => new MacTextBox();
}
// 3. OBSERVER PATTERN (Event-based)
public interface IObserver
{
void Update(string message);
}
public class EmailNotifier : IObserver
{
public void Update(string message) => Console.WriteLine($"Email sent: {message}");
}
public class SMSNotifier : IObserver
{
public void Update(string message) => Console.WriteLine($"SMS sent: {message}");
}
public class Subject
{
private List<IObserver> _observers = new List<IObserver>();
private string _state;
public void Attach(IObserver observer)
{
_observers.Add(observer);
}
public void Detach(IObserver observer)
{
_observers.Remove(observer);
}
public void Notify()
{
foreach (var observer in _observers)
{
observer.Update(_state);
}
}
public void SetState(string state)
{
_state = state;
Notify();
}
}
// 4. STRATEGY PATTERN
public interface IPaymentStrategy
{
void Pay(decimal amount);
}
public class CreditCardPayment : IPaymentStrategy
{
private string _cardNumber;
public CreditCardPayment(string cardNumber)
{
_cardNumber = cardNumber;
}
public void Pay(decimal amount)
{
Console.WriteLine($"Paid {amount:C} using Credit Card {_cardNumber}");
}
}
public class PayPalPayment : IPaymentStrategy
{
private string _email;
public PayPalPayment(string email)
{
_email = email;
}
public void Pay(decimal amount)
{
Console.WriteLine($"Paid {amount:C} using PayPal account {_email}");
}
}
public class CryptoPayment : IPaymentStrategy
{
private string _walletAddress;
public CryptoPayment(string walletAddress)
{
_walletAddress = walletAddress;
}
public void Pay(decimal amount)
{
Console.WriteLine($"Paid {amount:C} in crypto to {_walletAddress}");
}
}
public class Order
{
private IPaymentStrategy _paymentStrategy;
public void SetPaymentStrategy(IPaymentStrategy strategy)
{
_paymentStrategy = strategy;
}
public void Checkout(decimal amount)
{
_paymentStrategy?.Pay(amount);
}
}
// 5. REPOSITORY PATTERN (Data Access)
public interface IRepository<T> where T : IEntity
{
T GetById(int id);
IEnumerable<T> GetAll();
void Add(T entity);
void Update(T entity);
void Delete(int id);
}
public interface IEntity
{
int Id { get; set; }
}
public class Product : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class InMemoryRepository<T> : IRepository<T> where T : IEntity
{
private readonly Dictionary<int, T> _storage = new Dictionary<int, T>();
private int _nextId = 1;
public T GetById(int id) => _storage.TryGetValue(id, out var entity) ? entity : default;
public IEnumerable<T> GetAll() => _storage.Values;
public void Add(T entity)
{
entity.Id = _nextId++;
_storage[entity.Id] = entity;
}
public void Update(T entity)
{
if (_storage.ContainsKey(entity.Id))
_storage[entity.Id] = entity;
}
public void Delete(int id)
{
_storage.Remove(id);
}
}
// 6. BUILDER PATTERN
public class Computer
{
public string CPU { get; set; }
public string RAM { get; set; }
public string Storage { get; set; }
public string GPU { get; set; }
public bool HasWiFi { get; set; }
public bool HasBluetooth { get; set; }
public override string ToString()
{
return $"CPU: {CPU}, RAM: {RAM}, Storage: {Storage}, GPU: {GPU}, WiFi: {HasWiFi}, Bluetooth: {HasBluetooth}";
}
}
public interface IComputerBuilder
{
IComputerBuilder SetCPU(string cpu);
IComputerBuilder SetRAM(string ram);
IComputerBuilder SetStorage(string storage);
IComputerBuilder SetGPU(string gpu);
IComputerBuilder AddWiFi();
IComputerBuilder AddBluetooth();
Computer Build();
}
public class ComputerBuilder : IComputerBuilder
{
private Computer _computer = new Computer();
public IComputerBuilder SetCPU(string cpu)
{
_computer.CPU = cpu;
return this;
}
public IComputerBuilder SetRAM(string ram)
{
_computer.RAM = ram;
return this;
}
public IComputerBuilder SetStorage(string storage)
{
_computer.Storage = storage;
return this;
}
public IComputerBuilder SetGPU(string gpu)
{
_computer.GPU = gpu;
return this;
}
public IComputerBuilder AddWiFi()
{
_computer.HasWiFi = true;
return this;
}
public IComputerBuilder AddBluetooth()
{
_computer.HasBluetooth = true;
return this;
}
public Computer Build()
{
return _computer;
}
}
// 7. COMMAND PATTERN
public interface ICommand
{
void Execute();
void Undo();
}
public class Light
{
public void On() => Console.WriteLine("Light is ON");
public void Off() => Console.WriteLine("Light is OFF");
}
public class LightOnCommand : ICommand
{
private Light _light;
public LightOnCommand(Light light)
{
_light = light;
}
public void Execute() => _light.On();
public void Undo() => _light.Off();
}
public class RemoteControl
{
private ICommand _command;
private Stack<ICommand> _history = new Stack<ICommand>();
public void SetCommand(ICommand command)
{
_command = command;
}
public void PressButton()
{
_command.Execute();
_history.Push(_command);
}
public void PressUndo()
{
if (_history.Count > 0)
{
_history.Pop().Undo();
}
}
}
// 8. DECORATOR PATTERN
public interface ICoffee
{
decimal GetCost();
string GetDescription();
}
public class SimpleCoffee : ICoffee
{
public decimal GetCost() => 2.00m;
public string GetDescription() => "Simple coffee";
}
public abstract class CoffeeDecorator : ICoffee
{
protected ICoffee _coffee;
protected CoffeeDecorator(ICoffee coffee)
{
_coffee = coffee;
}
public virtual decimal GetCost() => _coffee.GetCost();
public virtual string GetDescription() => _coffee.GetDescription();
}
public class MilkDecorator : CoffeeDecorator
{
public MilkDecorator(ICoffee coffee) : base(coffee) { }
public override decimal GetCost() => _coffee.GetCost() + 0.50m;
public override string GetDescription() => _coffee.GetDescription() + ", milk";
}
public class SugarDecorator : CoffeeDecorator
{
public SugarDecorator(ICoffee coffee) : base(coffee) { }
public override decimal GetCost() => _coffee.GetCost() + 0.25m;
public override string GetDescription() => _coffee.GetDescription() + ", sugar";
}
// USAGE EXAMPLES
public void RunExamples()
{
Console.WriteLine("=== DESIGN PATTERNS DEMO ===\n");
// Singleton
Console.WriteLine("1. Singleton Pattern:");
var db1 = DatabaseConnection.Instance;
var db2 = DatabaseConnection.Instance;
Console.WriteLine($"Same instance? {ReferenceEquals(db1, db2)}\n");
// Factory
Console.WriteLine("2. Factory Pattern:");
IProductFactory factory = new ElectronicFactory();
var product = factory.CreateProduct();
Console.WriteLine($"Product: {product.GetName()}, Price: {product.GetPrice():C}\n");
// Abstract Factory
Console.WriteLine("3. Abstract Factory Pattern:");
IGUIFactory uiFactory = new WindowsFactory();
var button = uiFactory.CreateButton();
var textBox = uiFactory.CreateTextBox();
button.Render();
textBox.Render();
Console.WriteLine();
// Observer
Console.WriteLine("4. Observer Pattern:");
var subject = new Subject();
subject.Attach(new EmailNotifier());
subject.Attach(new SMSNotifier());
subject.SetState("System updated");
Console.WriteLine();
// Strategy
Console.WriteLine("5. Strategy Pattern:");
var order = new Order();
order.SetPaymentStrategy(new CreditCardPayment("1234-5678"));
order.Checkout(100.00m);
order.SetPaymentStrategy(new PayPalPayment("user@example.com"));
order.Checkout(50.00m);
Console.WriteLine();
// Builder
Console.WriteLine("6. Builder Pattern:");
var computer = new ComputerBuilder()
.SetCPU("Intel i9")
.SetRAM("32GB")
.SetStorage("1TB SSD")
.SetGPU("NVIDIA RTX 4080")
.AddWiFi()
.Build();
Console.WriteLine(computer);
Console.WriteLine();
// Command
Console.WriteLine("7. Command Pattern:");
var light = new Light();
var lightOn = new LightOnCommand(light);
var remote = new RemoteControl();
remote.SetCommand(lightOn);
remote.PressButton();
remote.PressUndo();
Console.WriteLine();
// Decorator
Console.WriteLine("8. Decorator Pattern:");
ICoffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
Console.WriteLine($"{coffee.GetDescription()}: {coffee.GetCost():C}");
}
}
// WHEN TO USE PATTERNS
// Singleton: One instance needed (logging, configuration)
// Factory: Complex object creation (different types)
// Observer: Event-driven systems (pub-sub)
// Strategy: Multiple algorithms (payment, sorting)
// Repository: Data access abstraction
// Builder: Complex objects with many configurations
// Command: Request queuing, undo/redo
// Decorator: Adding features dynamically
66. Unit Testing (xUnit, NUnit, Moq)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
// Note: These are example test structures
// To run actual tests, need to add NuGet packages:
// Install-Package xunit
// Install-Package Moq
// Install-Package FluentAssertions
public class UnitTestingDemo
{
// SAMPLE CLASS TO TEST
public class Calculator
{
public int Add(int a, int b) => a + b;
public int Subtract(int a, int b) => a - b;
public int Multiply(int a, int b) => a * b;
public double Divide(int a, int b)
{
if (b == 0) throw new DivideByZeroException("Cannot divide by zero");
return (double)a / b;
}
public bool IsEven(int number) => number % 2 == 0;
public int Factorial(int n)
{
if (n < 0) throw new ArgumentException("Factorial not defined for negative numbers");
if (n == 0) return 1;
return n * Factorial(n - 1);
}
}
// SAMPLE CLASS WITH DEPENDENCIES
public interface IEmailService
{
Task<bool> SendEmailAsync(string to, string subject, string body);
}
public interface IUserRepository
{
Task<User> GetUserByIdAsync(int id);
Task<bool> UserExistsAsync(string email);
Task AddUserAsync(User user);
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public bool IsActive { get; set; }
}
public class UserRegistrationService
{
private readonly IUserRepository _userRepository;
private readonly IEmailService _emailService;
public UserRegistrationService(IUserRepository userRepository, IEmailService emailService)
{
_userRepository = userRepository;
_emailService = emailService;
}
public async Task<bool> RegisterUserAsync(string name, string email)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("Name is required");
if (string.IsNullOrWhiteSpace(email))
throw new ArgumentException("Email is required");
if (await _userRepository.UserExistsAsync(email))
return false;
var user = new User { Name = name, Email = email, IsActive = true };
await _userRepository.AddUserAsync(user);
await _emailService.SendEmailAsync(email, "Welcome!", $"Welcome {name}!");
return true;
}
}
}
// XUNIT TESTS (Example test class)
/*
public class CalculatorTests
{
private readonly Calculator _calculator;
public CalculatorTests()
{
_calculator = new Calculator();
}
[Fact]
public void Add_ShouldReturnSum_WhenTwoNumbersAreProvided()
{
// Arrange
int a = 5, b = 3;
int expected = 8;
// Act
int result = _calculator.Add(a, b);
// Assert
Assert.Equal(expected, result);
}
[Theory]
[InlineData(2, 3, 6)]
[InlineData(0, 5, 0)]
[InlineData(-2, 3, -6)]
public void Multiply_ShouldReturnProduct_WhenNumbersAreProvided(int a, int b, int expected)
{
// Act
int result = _calculator.Multiply(a, b);
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void Divide_ShouldThrowException_WhenDividingByZero()
{
// Act & Assert
Assert.Throws<DivideByZeroException>(() => _calculator.Divide(10, 0));
}
[Theory]
[InlineData(5, 120)]
[InlineData(0, 1)]
[InlineData(1, 1)]
public void Factorial_ShouldReturnCorrectValue_WhenNumberIsValid(int n, int expected)
{
// Act
int result = _calculator.Factorial(n);
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void Factorial_ShouldThrowException_WhenNegativeNumber()
{
Assert.Throws<ArgumentException>(() => _calculator.Factorial(-1));
}
}
// MOQ TESTS (Mocking dependencies)
public class UserRegistrationTests
{
private readonly Mock<IUserRepository> _mockRepository;
private readonly Mock<IEmailService> _mockEmailService;
private readonly UserRegistrationService _service;
public UserRegistrationTests()
{
_mockRepository = new Mock<IUserRepository>();
_mockEmailService = new Mock<IEmailService>();
_service = new UserRegistrationService(_mockRepository.Object, _mockEmailService.Object);
}
[Fact]
public async Task RegisterUserAsync_ShouldReturnFalse_WhenUserAlreadyExists()
{
// Arrange
string name = "John Doe";
string email = "john@example.com";
_mockRepository.Setup(x => x.UserExistsAsync(email))
.ReturnsAsync(true);
// Act
bool result = await _service.RegisterUserAsync(name, email);
// Assert
Assert.False(result);
_mockRepository.Verify(x => x.AddUserAsync(It.IsAny<User>()), Times.Never);
_mockEmailService.Verify(x => x.SendEmailAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never);
}
[Fact]
public async Task RegisterUserAsync_ShouldCreateUser_WhenUserDoesNotExist()
{
// Arrange
string name = "Jane Doe";
string email = "jane@example.com";
_mockRepository.Setup(x => x.UserExistsAsync(email))
.ReturnsAsync(false);
_mockEmailService.Setup(x => x.SendEmailAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.ReturnsAsync(true);
// Act
bool result = await _service.RegisterUserAsync(name, email);
// Assert
Assert.True(result);
_mockRepository.Verify(x => x.AddUserAsync(It.Is<User>(u => u.Name == name && u.Email == email)), Times.Once);
_mockEmailService.Verify(x => x.SendEmailAsync(email, "Welcome!", It.Is<string>(msg => msg.Contains(name))), Times.Once);
}
[Fact]
public async Task RegisterUserAsync_ShouldThrowException_WhenNameIsEmpty()
{
// Act & Assert
await Assert.ThrowsAsync<ArgumentException>(() => _service.RegisterUserAsync("", "test@example.com"));
}
}
// NUNIT TESTS (Alternative syntax)
[NUnit.Framework.TestFixture]
public class CalculatorNUnitTests
{
private Calculator _calculator;
[NUnit.Framework.SetUp]
public void SetUp()
{
_calculator = new Calculator();
}
[NUnit.Framework.Test]
public void Add_ShouldReturnSum()
{
int result = _calculator.Add(5, 3);
NUnit.Framework.Assert.AreEqual(8, result);
}
[NUnit.Framework.TestCase(2, 3, 6)]
[NUnit.Framework.TestCase(0, 5, 0)]
public void Multiply_ShouldReturnProduct(int a, int b, int expected)
{
int result = _calculator.Multiply(a, b);
NUnit.Framework.Assert.AreEqual(expected, result);
}
}
// INTEGRATION TESTS
public class DatabaseIntegrationTests : IDisposable
{
private readonly string _connectionString;
private readonly DbContext _context;
public DatabaseIntegrationTests()
{
// Use test database
_connectionString = "Server=localhost;Database=TestDB;";
_context = new DbContext(_connectionString);
_context.Database.EnsureCreated();
}
[Fact]
public async Task AddUser_ShouldSaveToDatabase()
{
// Arrange
var user = new User { Name = "Test User", Email = "test@example.com" };
// Act
await _context.Users.AddAsync(user);
await _context.SaveChangesAsync();
// Assert
var savedUser = await _context.Users.FirstOrDefaultAsync(u => u.Email == "test@example.com");
Assert.NotNull(savedUser);
Assert.Equal("Test User", savedUser.Name);
}
public void Dispose()
{
// Clean up test database
_context.Database.EnsureDeleted();
_context.Dispose();
}
}
*/
Module 6 Summary
You've learned advanced .NET technologies:
Core Technologies (61-63)
- ✅ Reflection - Runtime type inspection and invocation
- ✅ Attributes - Metadata for runtime behavior
- ✅ Dynamic - Runtime type resolution (ExpandoObject, COM)
Architecture (64-65)
- ✅ Dependency Injection - Loose coupling, testability
- ✅ Design Patterns - Singleton, Factory, Strategy, Repository, Builder, Command, Decorator
Testing & Data (66)
- ✅ Unit Testing - xUnit, NUnit, Moq for mocking
- ✅ Integration Testing - Database testing patterns
Practice Exercises for Module 6
Exercise 1: Plugin System using Reflection
// Build a plugin system that:
// - Scans assemblies for types with [Plugin] attribute
// - Implements IPlugin interface
// - Dynamically loads and executes plugins
// - Supports plugin configuration
Exercise 2: Custom Validation Framework
// Create validation framework with:
// - Custom validation attributes (Required, MaxLength, Email)
// - Fluent validation syntax
// - Cross-property validation
// - Async validation support
Exercise 3: Dependency Injection Container
// Implement your own DI container with:
// - Transient, Scoped, Singleton lifetimes
// - Constructor injection
// - Factory registration
// - Open generics support
Complete C# Journey Complete! 🎉
You've now learned:
- Module 0: C# Basics
- Module 1: OOP Fundamentals
- Module 2: Advanced C# Features
- Module 3: Modern C# (6.0-12.0)
- Module 4: .NET Runtime Deep Dive
- Module 5: Asynchronous Programming
- Module 6: Advanced .NET Technologies
Next Steps Recommendations
For Interview Preparation
- Review all modules - focus on OOP, Collections, Async, LINQ
- Practice coding challenges on LeetCode/HackerRank
- Understand common interview questions from each module
- Build a portfolio project showcasing multiple concepts
For Real-World Development
- Build a REST API with ASP.NET Core
- Learn Entity Framework Core for data access
- Study Microservices architecture
- Learn Azure/AWS cloud services
- Practice with Docker containers
Recommended Resources
- Books: "C# in Depth" by Jon Skeet, "CLR via C#" by Jeffrey Richter
- Online: Microsoft Learn, Pluralsight, Udemy
- Practice: Exercism, Codewars, Advent of Code