1. Getting Started with C#
1.1 What is C#?
C# is a statically-typed, multi-paradigm programming language developed by Microsoft as part of its .NET framework. It's used for a variety of applications including web, mobile, and desktop applications.
1.2 Setting Up the Environment
To get started with C#, you’ll need:
- Visual Studio: This is the integrated development environment (IDE) used for C# development. You can download it from the Visual Studio website.
- .NET SDK: This contains the tools and libraries needed to build and run .NET applications.
1.3 Writing Your First Program
Create a new Console Application in Visual Studio and replace the content of Program.cs
with:
csharpusing System;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
}
Explanation:
using System;
imports the System namespace which contains fundamental classes likeConsole
.namespace HelloWorld
defines a scope that contains theProgram
class.class Program
defines a class named Program.static void Main(string[] args)
is the entry point of the application.
2. Basic Concepts
2.1 Variables and Data Types
C# is a strongly-typed language, meaning you need to declare the type of data a variable will hold.
csharpint number = 10;
double price = 19.95;
char letter = 'A';
string message = "Hello, C#";
bool isTrue = true;
Examples:
csharpint age = 30; // Integer
double height = 5.9; // Double-precision floating point
char initial = 'J'; // Single character
string name = "John Doe"; // String
bool isStudent = false; // Boolean
2.2 Control Structures
If-Else Statement:
csharpint number = 10;
if (number > 0)
{
Console.WriteLine("Positive number");
}
else if (number < 0)
{
Console.WriteLine("Negative number");
}
else
{
Console.WriteLine("Zero");
}
Switch Statement:
csharpint day = 3;
switch (day)
{
case 1:
Console.WriteLine("Monday");
break;
case 2:
Console.WriteLine("Tuesday");
break;
case 3:
Console.WriteLine("Wednesday");
break;
default:
Console.WriteLine("Invalid day");
break;
}
Loops:
For Loop:
csharpfor (int i = 0; i < 5; i++)
{
Console.WriteLine(i);
}
While Loop:
csharpint count = 0;
while (count < 5)
{
Console.WriteLine(count);
count++;
}
Do-While Loop:
csharpint num = 0;
do
{
Console.WriteLine(num);
num++;
} while (num < 5);
3. Object-Oriented Programming (OOP)
3.1 Classes and Objects
Creating a Class:
csharppublic class Car
{
public string Make { get; set; }
public string Model { get; set; }
public void DisplayInfo()
{
Console.WriteLine($"Make: {Make}, Model: {Model}");
}
}
Using a Class:
csharpclass Program
{
static void Main(string[] args)
{
Car myCar = new Car();
myCar.Make = "Toyota";
myCar.Model = "Corolla";
myCar.DisplayInfo();
}
}
3.2 Inheritance
Base Class:
csharppublic class Animal
{
public virtual void Speak()
{
Console.WriteLine("Animal makes a sound");
}
}
Derived Class:
csharppublic class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Dog barks");
}
}
Using Inheritance:
csharpclass Program
{
static void Main(string[] args)
{
Animal myAnimal = new Dog();
myAnimal.Speak(); // Output: Dog barks
}
}
3.3 Polymorphism
Polymorphism allows methods to do different things based on the object it is acting upon.
Method Overloading:
csharppublic class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
public double Add(double a, double b)
{
return a + b;
}
}
3.4 Encapsulation
Encapsulation involves hiding the internal state of an object and requiring all interaction to be performed through an object's methods.
csharppublic class Person
{
private int age;
public void SetAge(int value)
{
if (value > 0)
{
age = value;
}
}
public int GetAge()
{
return age;
}
}
4. Advanced Topics
4.1 Delegates and Events
Delegate:
csharppublic delegate void Notify(); // Delegate declaration
public class Process
{
public event Notify ProcessCompleted; // Event declaration
public void StartProcess()
{
// Process logic here
OnProcessCompleted();
}
protected virtual void OnProcessCompleted()
{
ProcessCompleted?.Invoke(); // Invoke the event
}
}
Using Delegates and Events:
csharpclass Program
{
static void Main(string[] args)
{
Process process = new Process();
process.ProcessCompleted += () => Console.WriteLine("Process completed.");
process.StartProcess();
}
}
4.2 LINQ (Language Integrated Query)
LINQ is a feature in C# that allows you to perform queries on arrays, collections, databases, and more.
Example:
csharpList<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var evenNumbers = from num in numbers
where num % 2 == 0
select num;
foreach (var num in evenNumbers)
{
Console.WriteLine(num);
}
4.3 Asynchronous Programming
Asynchronous programming allows your program to perform operations concurrently, improving efficiency.
Using async and await:
csharppublic async Task<string> GetDataAsync()
{
await Task.Delay(2000); // Simulate a delay
return "Data fetched";
}
public async Task MainAsync()
{
string data = await GetDataAsync();
Console.WriteLine(data);
}
5. Working with .NET Framework
5.1 .NET Core and .NET 5/6+
.NET Core and .NET 5/6+ are cross-platform frameworks. They are designed to run on multiple operating systems including Windows, macOS, and Linux.
Creating a .NET Core Web API:
Create a new Web API project in Visual Studio.
Define a model:
csharppublic class Product { public int Id { get; set; } public string Name { get; set; } }
Create a controller:
csharp[ApiController] [Route("api/[controller]")] public class ProductsController : ControllerBase { [HttpGet] public ActionResult<IEnumerable<Product>> Get() { return new List<Product> { new Product { Id = 1, Name = "Laptop" }, new Product { Id = 2, Name = "Smartphone" } }; } }
Run the API and test using Postman or similar tool.
This guide covers the core topics you’ll need to understand C# and .NET programming. As you progress, you can explore more specialized areas like Entity Framework, ASP.NET Core, Xamarin for mobile development, and more. Practice regularly and build small projects to solidify your understanding. If you have specific questions or need further details on any topic, feel free to ask!
Certainly! Let’s dive deeper into more advanced C# and .NET concepts. We’ll expand on some of the areas we’ve covered and introduce new topics to give you a comprehensive understanding of C# .NET.
6. Advanced Object-Oriented Programming
6.1 Abstract Classes and Methods
Abstract classes cannot be instantiated and can contain abstract methods which must be implemented by derived classes.
Abstract Class Example:
csharppublic abstract class Shape
{
public abstract double Area(); // Abstract method
public void Display()
{
Console.WriteLine($"The area is {Area()}");
}
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public override double Area()
{
return Width * Height;
}
}
Usage:
csharpclass Program
{
static void Main(string[] args)
{
Shape shape = new Rectangle { Width = 5, Height = 10 };
shape.Display(); // Output: The area is 50
}
}
6.2 Interfaces
Interfaces define a contract for classes to implement. They cannot contain implementation, only method signatures.
Interface Example:
csharppublic interface IShape
{
double Area();
void Display();
}
public class Circle : IShape
{
public double Radius { get; set; }
public double Area()
{
return Math.PI * Radius * Radius;
}
public void Display()
{
Console.WriteLine($"The area of the circle is {Area()}");
}
}
Usage:
csharpclass Program
{
static void Main(string[] args)
{
IShape shape = new Circle { Radius = 7 };
shape.Display(); // Output: The area of the circle is 153.93804002589985
}
}
6.3 Composition over Inheritance
Composition involves building classes that use other classes, instead of extending them.
Example:
csharppublic class Engine
{
public void Start() => Console.WriteLine("Engine started");
}
public class Car
{
private Engine _engine = new Engine();
public void Start()
{
_engine.Start();
Console.WriteLine("Car started");
}
}
Usage:
csharpclass Program
{
static void Main(string[] args)
{
Car car = new Car();
car.Start(); // Output: Engine started
// Car started
}
}
7. Exception Handling
Handling exceptions gracefully is crucial for robust applications.
7.1 Try-Catch-Finally
Example:
csharptry
{
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // Will throw an exception
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine($"Exception: {ex.Message}");
}
finally
{
Console.WriteLine("This block is always executed.");
}
7.2 Custom Exceptions
You can define your own exception types.
Example:
csharppublic class CustomException : Exception
{
public CustomException(string message) : base(message) { }
}
Usage:
csharpclass Program
{
static void Main(string[] args)
{
try
{
throw new CustomException("This is a custom exception");
}
catch (CustomException ex)
{
Console.WriteLine($"Caught: {ex.Message}");
}
}
}
8. Collections and Generics
8.1 Collections
List<T>
csharpList<string> names = new List<string> { "Alice", "Bob", "Charlie" };
names.Add("David");
foreach (var name in names)
{
Console.WriteLine(name);
}
Dictionary<TKey, TValue>
csharpDictionary<string, int> ageDictionary = new Dictionary<string, int>
{
{ "Alice", 30 },
{ "Bob", 25 }
};
ageDictionary["Charlie"] = 35;
foreach (var kvp in ageDictionary)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
8.2 Generics
Generics allow you to define classes, methods, and interfaces with a placeholder for the data type.
Generic Class Example:
csharppublic class Box<T>
{
private T _content;
public void SetContent(T content)
{
_content = content;
}
public T GetContent()
{
return _content;
}
}
Usage:
csharpclass Program
{
static void Main(string[] args)
{
Box<int> intBox = new Box<int>();
intBox.SetContent(123);
Console.WriteLine(intBox.GetContent()); // Output: 123
Box<string> strBox = new Box<string>();
strBox.SetContent("Hello Generics");
Console.WriteLine(strBox.GetContent()); // Output: Hello Generics
}
}
9. File I/O and Serialization
9.1 File Operations
Reading and Writing Files:
csharp// Writing to a file
File.WriteAllText("example.txt", "Hello, File!");
// Reading from a file
string content = File.ReadAllText("example.txt");
Console.WriteLine(content); // Output: Hello, File!
9.2 Serialization
Serialization converts objects into a format that can be easily stored or transmitted.
JSON Serialization:
csharpusing System.Text.Json;
// Define a class
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
// Serialize object to JSON
Person person = new Person { Name = "John", Age = 30 };
string json = JsonSerializer.Serialize(person);
Console.WriteLine(json); // Output: {"Name":"John","Age":30}
// Deserialize JSON to object
Person deserializedPerson = JsonSerializer.Deserialize<Person>(json);
Console.WriteLine($"{deserializedPerson.Name}, {deserializedPerson.Age}"); // Output: John, 30
10. Dependency Injection
Dependency Injection (DI) is a design pattern that helps to achieve Inversion of Control (IoC) by injecting dependencies rather than hard-coding them.
10.1 Constructor Injection
Setup:
csharppublic interface IService
{
void Serve();
}
public class Service : IService
{
public void Serve() => Console.WriteLine("Service Called");
}
public class Client
{
private readonly IService _service;
public Client(IService service)
{
_service = service;
}
public void Start()
{
_service.Serve();
}
}
Usage:
csharpclass Program
{
static void Main(string[] args)
{
IService service = new Service();
Client client = new Client(service);
client.Start(); // Output: Service Called
}
}
10.2 Using .NET Core Dependency Injection
In ASP.NET Core applications, you can use built-in dependency injection.
Configure Services in Startup.cs:
csharppublic void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IService, Service>();
}
Injecting Dependencies:
csharppublic class HomeController : Controller
{
private readonly IService _service;
public HomeController(IService service)
{
_service = service;
}
public IActionResult Index()
{
_service.Serve();
return View();
}
}
11. Advanced LINQ
11.1 LINQ Query Syntax vs. Method Syntax
Query Syntax:
csharpvar result = from num in numbers
where num > 2
select num;
Method Syntax:
csharpvar result = numbers.Where(num => num > 2);
11.2 LINQ to Entities
LINQ can be used with Entity Framework to query databases.
Example with Entity Framework:
csharpusing (var context = new MyDbContext())
{
var products = context.Products
.Where(p => p.Price > 20)
.ToList();
}
12. Multithreading and Parallelism
12.1 Threads
Creating a Thread:
csharpThread newThread = new Thread(() =>
{
Console.WriteLine("Thread running");
});
newThread.Start();
12.2 Task Parallel Library (TPL)
Creating and Running Tasks:
csharpTask.Run(() =>
{
Console.WriteLine("Task running");
});
Awaiting a Task:
csharppublic async Task DoWorkAsync()
{
await Task.Run(() =>
{
// Simulate work
Thread.Sleep(2000);
});
}
13. Unit Testing
13.1 Writing Tests with xUnit
Example Test Class:
csharppublic class CalculatorTests
{
[Fact]
public void Add_TwoNumbers_ReturnsSum()
{
// Arrange
Calculator calculator = new Calculator();
int a = 5;
int b = 3;
// Act
int result = calculator.Add(a, b);
// Assert
Assert.Equal(8, result);
}
}
Running Tests:
You can run your tests from within Visual Studio or using the .NET CLI with dotnet test
.
14. ASP.NET Core
14.1 Creating a Web Application
Creating a Web Application:
Create a new ASP.NET Core Web Application in Visual Studio.
Define a Controller:
csharp[ApiController] [Route("api/[controller]")] public class WeatherController : ControllerBase { [HttpGet] public ActionResult<string> GetWeather() { return "Sunny"; } }
Configuring Services and Middleware:
csharppublic class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
15. Entity Framework Core
15.1 Setting Up Entity Framework Core
Install EF Core Packages:
bashdotnet add package Microsoft.EntityFrameworkCore dotnet add package Microsoft.EntityFrameworkCore.SqlServer
Define a DbContext and Entities:
csharppublic class AppDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
Using DbContext:
csharppublic class ProductService
{
private readonly AppDbContext _context;
public ProductService(AppDbContext context)
{
_context = context;
}
public async Task<List<Product>> GetProductsAsync()
{
return await _context.Products.ToListAsync();
}
}
16. Security
16.1 Authentication and Authorization
Adding Authentication to an ASP.NET Core Application:
Install the required packages:
bashdotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Configure Authentication:
csharppublic void ConfigureServices(IServiceCollection services) { services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = "yourIssuer", ValidAudience = "yourAudience", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("yourSecretKey")) }; }); }
Applying Authorization:
csharp[Authorize]
public class SecureController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok("This is a secure endpoint");
}
}
16.2 Data Protection
Using Data Protection:
csharppublic class DataProtectionService
{
private readonly IDataProtector _protector;
public DataProtectionService(IDataProtectionProvider provider)
{
_protector = provider.CreateProtector("DataProtectionService");
}
public string Protect(string data)
{
return _protector.Protect(data);
}
public string Unprotect(string protectedData)
{
return _protector.Unprotect(protectedData);
}
}
This extended guide should provide a more comprehensive overview of advanced C# and .NET concepts. As you continue to learn, consider exploring specialized topics such as microservices, advanced performance optimization, or cloud services integration. Practice is key, so keep building projects and experimenting with different features. If you have specific questions or need more detailed examples on any topic, feel free to ask!
Certainly! Let's delve into even more advanced topics and concepts within C# and .NET to deepen your expertise. We’ll cover a range of topics including advanced language features, modern .NET capabilities, and best practices.
17. Advanced C# Language Features
17.1 Pattern Matching
Pattern matching allows more expressive and readable ways to handle complex conditions.
Type Patterns:
csharpobject obj = "Hello";
if (obj is string str)
{
Console.WriteLine($"String length: {str.Length}");
}
Switch Expressions:
csharpint number = 3;
string result = number switch
{
1 => "One",
2 => "Two",
3 => "Three",
_ => "Unknown"
};
Console.WriteLine(result); // Output: Three
Property Patterns:
csharppublic class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
Person person = new Person { Name = "Alice", Age = 30 };
if (person is { Name: "Alice", Age: > 20 })
{
Console.WriteLine("Alice is over 20 years old");
}
17.2 Records
Records provide a concise syntax for immutable data objects.
Defining a Record:
csharppublic record Person(string Name, int Age);
Usage:
csharpPerson person = new Person("Bob", 25);
Person anotherPerson = person with { Age = 26 };
Console.WriteLine(person); // Output: Person { Name = Bob, Age = 25 }
Console.WriteLine(anotherPerson); // Output: Person { Name = Bob, Age = 26 }
17.3 Nullable Reference Types
Nullable reference types help avoid null reference exceptions.
Enabling Nullable Context:
csharp#nullable enable
public class MyClass
{
public string? Name { get; set; } // Nullable property
}
Usage:
csharpMyClass obj = new MyClass();
obj.Name = null; // Allowed
17.4 Local Functions
Local functions are functions defined inside other methods, useful for encapsulating helper logic.
Example:
csharppublic void ProcessData()
{
void Print(string message)
{
Console.WriteLine(message);
}
Print("Processing data...");
}
18. Modern .NET Capabilities
18.1 .NET 6/7+ Features
.NET 6 and later versions bring several new features and improvements.
Minimal APIs (in .NET 6+):
csharpvar builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello, World!");
app.Run();
File-scoped Namespace Declaration:
csharpnamespace MyNamespace;
public class MyClass
{
// Class members
}
Global Using Directives:
csharpglobal using System;
global using System.Collections.Generic;
18.2 Blazor
Blazor allows building interactive web UIs with C# instead of JavaScript.
Blazor Component Example:
csharp@page "/counter"
@using Microsoft.AspNetCore.Components.Web
<h3>Counter</h3>
<p>Current count: @currentCount</p>
<button @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
18.3 ASP.NET Core Minimal APIs
Minimal APIs simplify the process of building small HTTP APIs.
Example:
csharpvar builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/hello", () => "Hello, Minimal API!");
app.Run();
19. Advanced Entity Framework Core
19.1 Migrations
Migrations allow you to manage schema changes in your database.
Creating a Migration:
bashdotnet ef migrations add InitialCreate
Applying Migrations:
bashdotnet ef database update
19.2 Query Performance and Optimization
Using AsNoTracking:
csharpvar products = await context.Products.AsNoTracking().ToListAsync();
Optimizing Queries with Eager Loading:
csharpvar orders = await context.Orders.Include(o => o.Customer).ToListAsync();
19.3 Complex Types and Value Objects
Defining a Complex Type:
csharppublic class Address
{
public string Street { get; set; }
public string City { get; set; }
}
Configuring Complex Types:
csharpprotected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>()
.OwnsOne(c => c.Address);
}
20. Design Patterns
20.1 Singleton Pattern
Ensures a class has only one instance and provides a global point of access.
Example:
csharppublic class Singleton
{
private static Singleton _instance;
private static readonly object _lock = new object();
private Singleton() { }
public static Singleton Instance
{
get
{
lock (_lock)
{
if (_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
}
}
}
20.2 Repository Pattern
Provides a way to manage data access logic.
Repository Interface:
csharppublic interface IProductRepository
{
Task<Product> GetByIdAsync(int id);
Task<IEnumerable<Product>> GetAllAsync();
}
Repository Implementation:
csharppublic class ProductRepository : IProductRepository
{
private readonly AppDbContext _context;
public ProductRepository(AppDbContext context)
{
_context = context;
}
public async Task<Product> GetByIdAsync(int id)
{
return await _context.Products.FindAsync(id);
}
public async Task<IEnumerable<Product>> GetAllAsync()
{
return await _context.Products.ToListAsync();
}
}
20.3 Unit of Work Pattern
Manages multiple repositories and ensures data consistency.
Unit of Work Interface:
csharppublic interface IUnitOfWork : IDisposable
{
IProductRepository Products { get; }
Task<int> CompleteAsync();
}
Unit of Work Implementation:
csharppublic class UnitOfWork : IUnitOfWork
{
private readonly AppDbContext _context;
public IProductRepository Products { get; private set; }
public UnitOfWork(AppDbContext context)
{
_context = context;
Products = new ProductRepository(context);
}
public async Task<int> CompleteAsync()
{
return await _context.SaveChangesAsync();
}
public void Dispose()
{
_context.Dispose();
}
}
21. Performance and Optimization
21.1 Profiling and Diagnostics
Using Performance Profilers:
Tools like Visual Studio Profiler or JetBrains dotTrace can help identify performance bottlenecks.
Example Code for Measuring Time:
csharpvar stopwatch = Stopwatch.StartNew();
// Code to measure
stopwatch.Stop();
Console.WriteLine($"Elapsed Time: {stopwatch.ElapsedMilliseconds} ms");
21.2 Memory Management
Avoiding Memory Leaks:
- Use
IDisposable
to release unmanaged resources. - Monitor memory usage and garbage collection.
Example of IDisposable:
csharppublic class Resource : IDisposable
{
private bool _disposed = false;
public void Dispose()
{
if (!_disposed)
{
// Free unmanaged resources
_disposed = true;
}
GC.SuppressFinalize(this);
}
}
Using using
Statement:
csharpusing (var resource = new Resource())
{
// Use resource
}
21.3 Asynchronous Programming Best Practices
Avoiding Async Void:
Async methods should return Task
or Task<T>
, not void
, except for event handlers.
Example:
csharppublic async Task ProcessAsync()
{
await Task.Delay(1000);
}
22. Cloud and Microservices
22.1 Working with Azure
Deploying Applications:
- Use Azure App Service for web apps.
- Use Azure SQL Database for managed databases.
Example of Connecting to Azure SQL Database:
csharpvar connectionString = "Server=tcp:yourserver.database.windows.net,1433;Initial Catalog=yourdb;Persist Security Info=False;User ID=youruser;Password=yourpassword;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;";
using (var connection = new SqlConnection(connectionString))
{
await connection.OpenAsync();
// Perform database operations
}
22.2 Microservices Architecture
Using API Gateway:
- Centralized routing and load balancing.
- Handle cross-cutting concerns like authentication.
Example of Implementing a Simple API Gateway:
csharppublic class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddHttpClient();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/api", async context =>
{
// Proxy request to another service
var client = context.RequestServices.GetRequiredService<IHttpClientFactory>().CreateClient();
var response = await client.GetAsync("http://other-service/api");
var content = await response.Content.ReadAsStringAsync();
await context.Response.WriteAsync(content);
});
});
}
}
22.3 Docker and Containers
Creating a Dockerfile:
dockerfileFROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app EXPOSE 80 COPY . . ENTRYPOINT ["dotnet", "MyApp.dll"]
Building and Running Docker Container:
bashdocker build -t myapp . docker run -d -p 8080:80 myapp
23. Advanced Testing Techniques
23.1 Mocking Dependencies
Using Moq for Mocking:
csharpvar mockService = new Mock<IService>();
mockService.Setup(s => s.GetData()).Returns("Mock Data");
var service = mockService.Object;
Console.WriteLine(service.GetData()); // Output: Mock Data
23.2 Integration Testing
Integration Tests with TestServer:
csharppublic class IntegrationTests
{
private readonly HttpClient _client;
public IntegrationTests()
{
var webHostBuilder = new WebHostBuilder()
.UseStartup<Startup>();
var server = new TestServer(webHostBuilder);
_client = server.CreateClient();
}
[Fact]
public async Task Test_Get()
{
var response = await _client.GetStringAsync("/api/values");
Assert.Equal("value", response);
}
}
24. Advanced Security
24.1 OAuth and OpenID Connect
Setting Up OAuth in ASP.NET Core:
Configure IdentityServer4 or another OAuth provider.
Add Authentication in Startup.cs:
csharpservices.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie() .AddOpenIdConnect(options => { options.Authority = "https://identityserver"; options.ClientId = "client-id"; options.ClientSecret = "client-secret"; options.ResponseType = "code"; options.SaveTokens = true; });
24.2 Secure Coding Practices
- Sanitize User Inputs: Always validate and sanitize inputs to prevent injection attacks.
- Use HTTPS: Ensure data is encrypted during transmission.
- Implement Proper Error Handling: Avoid exposing stack traces or sensitive information in error responses.
These topics cover a wide range of advanced aspects in C# and .NET, from modern language features to cloud computing and secure coding practices. Exploring these areas will give you a well-rounded understanding of advanced .NET development. If you need more in-depth explanations or specific examples on any of these topics, feel free to ask!
Absolutely! Let’s explore even more advanced topics and details within C# and .NET to further enrich your knowledge. We’ll delve into more specialized areas, such as advanced asynchronous programming, complex application architectures, and performance optimization strategies.
25. Advanced Asynchronous Programming
25.1 ValueTask
ValueTask
is used for performance optimization when a task is completed synchronously or if the operation is typically very fast.
Example:
csharppublic async ValueTask<int> GetValueAsync()
{
if (/* some condition */)
{
return 42; // Fast path
}
else
{
await Task.Delay(1000); // Slow path
return 42;
}
}
Usage:
csharppublic async Task UseValueTask()
{
ValueTask<int> result = GetValueAsync();
int value = await result;
Console.WriteLine(value);
}
25.2 Async Streams
Async streams allow you to consume data asynchronously as it becomes available.
Example:
csharppublic async IAsyncEnumerable<int> GetNumbersAsync()
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(100); // Simulate asynchronous operation
yield return i;
}
}
Usage:
csharppublic async Task PrintNumbersAsync()
{
await foreach (int number in GetNumbersAsync())
{
Console.WriteLine(number);
}
}
25.3 Cancellation Tokens
Cancellation tokens allow you to cancel ongoing asynchronous operations.
Example:
csharppublic async Task LongRunningOperationAsync(CancellationToken cancellationToken)
{
for (int i = 0; i < 10; i++)
{
cancellationToken.ThrowIfCancellationRequested();
await Task.Delay(1000);
Console.WriteLine("Working...");
}
}
Usage:
csharppublic async Task RunOperationWithCancellationAsync()
{
var cts = new CancellationTokenSource();
var task = LongRunningOperationAsync(cts.Token);
// Cancel after 3 seconds
await Task.Delay(3000);
cts.Cancel();
try
{
await task;
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation was canceled.");
}
}
26. Complex Application Architectures
26.1 Microservices Architecture
Microservices architecture involves breaking down an application into smaller, independent services.
Example of a Microservice:
csharppublic class ProductService
{
public string GetProduct(int id)
{
// Retrieve product from database
return "Product Name";
}
}
API Gateway:
An API Gateway handles requests and routes them to appropriate microservices.
csharppublic class ApiGateway
{
private readonly HttpClient _httpClient;
public ApiGateway(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<string> GetProductAsync(int id)
{
var response = await _httpClient.GetStringAsync($"http://product-service/api/products/{id}");
return response;
}
}
26.2 Event-Driven Architecture
Event-driven architecture uses events to communicate between services.
Event Publisher:
csharppublic class OrderService
{
private readonly IEventPublisher _eventPublisher;
public OrderService(IEventPublisher eventPublisher)
{
_eventPublisher = eventPublisher;
}
public void PlaceOrder(Order order)
{
// Process order
_eventPublisher.Publish(new OrderPlacedEvent(order));
}
}
Event Subscriber:
csharppublic class InventoryService
{
public void OnOrderPlaced(OrderPlacedEvent orderPlacedEvent)
{
// Update inventory based on the order
}
}
26.3 Domain-Driven Design (DDD)
DDD emphasizes modeling the domain and its logic. It includes concepts like entities, aggregates, and repositories.
Example of an Aggregate Root:
csharppublic class Order
{
public int Id { get; private set; }
public List<OrderItem> Items { get; private set; } = new List<OrderItem>();
public void AddItem(OrderItem item)
{
Items.Add(item);
}
}
public class OrderItem
{
public int ProductId { get; private set; }
public int Quantity { get; private set; }
}
Repository Interface:
csharppublic interface IOrderRepository
{
Task<Order> GetByIdAsync(int id);
Task AddAsync(Order order);
}
27. Performance Optimization
27.1 Caching
Caching improves performance by storing frequently accessed data.
In-Memory Cache:
csharppublic class CacheService
{
private readonly IMemoryCache _cache;
public CacheService(IMemoryCache cache)
{
_cache = cache;
}
public void SetCache(string key, string value)
{
_cache.Set(key, value, TimeSpan.FromMinutes(5));
}
public string GetCache(string key)
{
return _cache.TryGetValue(key, out var value) ? (string)value : null;
}
}
Distributed Cache (Redis):
csharppublic class RedisCacheService
{
private readonly IDistributedCache _cache;
public RedisCacheService(IDistributedCache cache)
{
_cache = cache;
}
public async Task SetCacheAsync(string key, string value)
{
await _cache.SetStringAsync(key, value, new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
});
}
public async Task<string> GetCacheAsync(string key)
{
return await _cache.GetStringAsync(key);
}
}
27.2 Code Optimization
Avoiding Premature Optimization:
Focus on optimizing performance bottlenecks identified through profiling, not assumptions.
Efficient Data Structures:
Choose the right data structure based on use cases. For instance, Dictionary<TKey, TValue>
for fast lookups.
Example of Efficient Lookup:
csharppublic class ProductCatalog
{
private readonly Dictionary<int, Product> _products;
public ProductCatalog(IEnumerable<Product> products)
{
_products = products.ToDictionary(p => p.Id);
}
public Product GetProduct(int id)
{
_products.TryGetValue(id, out var product);
return product;
}
}
27.3 Asynchronous Processing and Parallelism
Using Parallel
Library:
csharpParallel.For(0, 10, i =>
{
Console.WriteLine($"Processing item {i}");
});
Task Parallel Library (TPL):
csharppublic async Task ProcessItemsAsync(List<string> items)
{
var tasks = items.Select(async item =>
{
await Task.Delay(100); // Simulate work
Console.WriteLine($"Processed {item}");
});
await Task.WhenAll(tasks);
}
28. Advanced Testing
28.1 Property-Based Testing
Property-based testing involves defining properties that should hold for a range of inputs.
Example with FsCheck:
csharp[Property]
public void ReverseReverseEqualsOriginal(string input)
{
var reversed = new string(input.Reverse().ToArray());
var doubleReversed = new string(reversed.Reverse().ToArray());
Assert.Equal(input, doubleReversed);
}
28.2 Test-Driven Development (TDD)
TDD involves writing tests before the actual implementation.
Steps:
Write a Failing Test:
csharp[Fact] public void Add_TwoNumbers_ReturnsSum() { var calculator = new Calculator(); var result = calculator.Add(2, 3); Assert.Equal(5, result); }
Write the Minimum Code to Pass the Test:
csharppublic class Calculator { public int Add(int a, int b) { return a + b; } }
Refactor: Refactor the code as necessary while ensuring that all tests pass.
29. Security Best Practices
29.1 Securing APIs
Implementing API Rate Limiting:
csharppublic class RateLimitMiddleware
{
private readonly RequestDelegate _next;
private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(10); // Limit to 10 requests
public RateLimitMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
await _semaphore.WaitAsync();
try
{
await _next(context);
}
finally
{
_semaphore.Release();
}
}
}
Adding Rate Limiting Middleware:
csharppublic void Configure(IApplicationBuilder app)
{
app.UseMiddleware<RateLimitMiddleware>();
// Other middlewares
}
29.2 Securing Sensitive Data
Encrypting Data:
csharppublic class EncryptionService
{
private readonly byte[] _key;
private readonly byte[] _iv;
public EncryptionService(byte[] key, byte[] iv)
{
_key = key;
_iv = iv;
}
public byte[] Encrypt(string plaintext)
{
using (var aes = Aes.Create())
{
aes.Key = _key;
aes.IV = _iv;
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
{
var data = Encoding.UTF8.GetBytes(plaintext);
return encryptor.TransformFinalBlock(data, 0, data.Length);
}
}
}
public string Decrypt(byte[] ciphertext)
{
using (var aes = Aes.Create())
{
aes.Key = _key;
aes.IV = _iv;
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
{
var data = decryptor.TransformFinalBlock(ciphertext, 0, ciphertext.Length);
return Encoding.UTF8.GetString(data);
}
}
}
}
30. Advanced Debugging Techniques
30.1 Debugging Asynchronous Code
Using Visual Studio’s Diagnostic Tools:
- Use the Tasks window to view running tasks.
- Set breakpoints in async methods and inspect the state.
Using Logging for Debugging:
csharppublic async Task ProcessDataAsync()
{
_logger.LogInformation("Started processing");
await Task.Delay(1000); // Simulate work
_logger.LogInformation("Finished processing");
}
30.2 Profiling and Performance Analysis
Using .NET Core Performance Counters:
- Use tools like PerfView to analyze performance counters.
- Look for GC stats, memory usage, and CPU usage.
Analyzing Memory Usage:
- Use tools like dotMemory to inspect heap snapshots and memory usage patterns.