# .NET Documentation The .NET Documentation repository (dotnet/docs) is the official source for conceptual documentation for the .NET platform. It provides comprehensive guides, tutorials, API references, and best practices for C#, F#, Visual Basic, .NET Core, .NET Framework, and related technologies. The documentation covers everything from language fundamentals to advanced topics like asynchronous programming, dependency injection, serialization, and cloud-native development. This repository serves as one of several repositories that build the .NET documentation site at learn.microsoft.com/dotnet. It focuses on conceptual content while API reference documentation is maintained in separate repositories. The content spans multiple .NET versions and includes breaking changes documentation, migration guides, and what's new articles for each release. ## C# Top-Level Statements Top-level statements allow you to write executable C# code directly at the file root without explicit `Main` method boilerplate. This feature is the default for new console applications and supports async/await, command-line arguments, and return codes. ```csharp // Program.cs - A complete C# program with top-level statements using System.Net.Http; // Access command-line arguments via the implicit 'args' variable if (args.Length > 0) { Console.WriteLine($"Arguments: {string.Join(", ", args)}"); } // Use async/await directly var client = new HttpClient(); var response = await client.GetStringAsync("https://api.github.com/zen"); Console.WriteLine($"GitHub Zen: {response}"); // Return an exit code return 0; // Types can be defined after top-level statements public record Person(string Name, int Age); ``` ## Asynchronous Programming with async/await The Task Asynchronous Programming (TAP) model provides high-level abstractions for asynchronous operations using async and await keywords. This enables writing non-blocking code that reads sequentially while executing asynchronously. ```csharp using System.Net.Http; public class DataService { private readonly HttpClient _client = new(); // Async method returning Task public async Task FetchDataAsync(string url, CancellationToken cancellationToken = default) { try { // await releases the thread while waiting var response = await _client.GetAsync(url, cancellationToken); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(cancellationToken); } catch (HttpRequestException ex) { Console.WriteLine($"Request failed: {ex.Message}"); throw; } } // Running multiple async operations concurrently public async Task FetchMultipleAsync(string[] urls) { // Start all tasks without awaiting var tasks = urls.Select(url => FetchDataAsync(url)); // Wait for all to complete return await Task.WhenAll(tasks); } // Process items as they complete public async Task ProcessAsCompletedAsync(string[] urls) { var tasks = urls.Select(url => FetchDataAsync(url)).ToList(); while (tasks.Count > 0) { var completed = await Task.WhenAny(tasks); tasks.Remove(completed); Console.WriteLine($"Completed: {await completed}"); } } } ``` ## LINQ Query Syntax and Method Syntax Language Integrated Query (LINQ) provides a consistent query experience across different data sources using either query syntax or method syntax with lambda expressions. ```csharp public class LinqExamples { record Student(string Name, int Grade, string[] Courses); public void DemonstrateLinq() { var students = new List { new("Alice", 95, new[] { "Math", "Physics" }), new("Bob", 82, new[] { "Chemistry", "Biology" }), new("Carol", 88, new[] { "Math", "Chemistry" }) }; // Query syntax - declarative approach var topStudents = from s in students where s.Grade >= 85 orderby s.Grade descending select new { s.Name, s.Grade }; // Method syntax - fluent approach (equivalent) var topStudentsMethod = students .Where(s => s.Grade >= 85) .OrderByDescending(s => s.Grade) .Select(s => new { s.Name, s.Grade }); // Grouping with query syntax var byGradeRange = from s in students group s by s.Grade / 10 into g select new { Range = g.Key * 10, Students = g.ToList() }; // Aggregation methods var average = students.Average(s => s.Grade); // 88.33 var max = students.Max(s => s.Grade); // 95 var count = students.Count(s => s.Grade > 80); // 3 // SelectMany - flatten nested collections var allCourses = students .SelectMany(s => s.Courses) .Distinct() .OrderBy(c => c); // Join operations var grades = new[] { (Name: "Alice", Score: 95), (Name: "Bob", Score: 82) }; var joined = from s in students join g in grades on s.Name equals g.Name select new { s.Name, s.Courses, g.Score }; // Deferred execution - query runs when enumerated foreach (var student in topStudents) { Console.WriteLine($"{student.Name}: {student.Grade}"); } } } ``` ## Dependency Injection .NET provides built-in dependency injection through `IServiceCollection` and `IServiceProvider`. Services can be registered with different lifetimes: Transient (new instance each request), Scoped (per scope), and Singleton (single instance). ```csharp using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; // Define service interfaces public interface IMessageService { string GetMessage(); } public interface INotificationService { void Notify(string message); } // Implement services public class EmailMessageService : IMessageService { public string GetMessage() => "Hello from Email Service!"; } public class ConsoleNotificationService : INotificationService { private readonly IMessageService _messageService; // Constructor injection public ConsoleNotificationService(IMessageService messageService) { _messageService = messageService; } public void Notify(string message) { Console.WriteLine($"[Notification] {_messageService.GetMessage()}: {message}"); } } // Program.cs - Configure and use DI var builder = Host.CreateApplicationBuilder(args); // Register services with different lifetimes builder.Services.AddTransient(); // New instance each time builder.Services.AddScoped(); // Per scope builder.Services.AddSingleton(); // Single instance for app lifetime // Register with factory method builder.Services.AddTransient(sp => new EmailMessageService()); var host = builder.Build(); // Resolve and use services using var scope = host.Services.CreateScope(); var notifier = scope.ServiceProvider.GetRequiredService(); notifier.Notify("Application started"); // Output: [Notification] Hello from Email Service!: Application started ``` ## JSON Serialization with System.Text.Json The `System.Text.Json` namespace provides high-performance JSON serialization and deserialization with support for customization through attributes and options. ```csharp using System.Text.Json; using System.Text.Json.Serialization; public class WeatherForecast { public DateTime Date { get; set; } [JsonPropertyName("temp_c")] // Custom JSON property name public int TemperatureCelsius { get; set; } public string? Summary { get; set; } [JsonIgnore] // Exclude from serialization public string? InternalNotes { get; set; } } public class JsonExamples { public void SerializeAndDeserialize() { var forecast = new WeatherForecast { Date = DateTime.Now, TemperatureCelsius = 25, Summary = "Warm and sunny", InternalNotes = "This won't be serialized" }; // Configure serialization options var options = new JsonSerializerOptions { WriteIndented = true, // Pretty print PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; // Serialize to string string json = JsonSerializer.Serialize(forecast, options); Console.WriteLine(json); // Output: // { // "date": "2024-01-15T10:30:00", // "temp_c": 25, // "summary": "Warm and sunny" // } // Serialize to UTF-8 bytes (faster) byte[] utf8Json = JsonSerializer.SerializeToUtf8Bytes(forecast, options); // Deserialize from string var deserialized = JsonSerializer.Deserialize(json, options); // Serialize to file using var stream = File.Create("forecast.json"); JsonSerializer.Serialize(stream, forecast, options); // Async file operations await using var readStream = File.OpenRead("forecast.json"); var fromFile = await JsonSerializer.DeserializeAsync(readStream, options); } } ``` ## Configuration in .NET The configuration system reads key-value pairs from multiple sources (JSON files, environment variables, command-line arguments) and provides a unified API through `IConfiguration`. ```csharp using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; // appsettings.json content: // { // "Database": { // "ConnectionString": "Server=localhost;Database=myapp", // "MaxRetries": 3 // }, // "Features": { // "EnableCache": true // } // } // Strongly-typed configuration class public class DatabaseSettings { public string ConnectionString { get; set; } = ""; public int MaxRetries { get; set; } = 1; } public class FeatureSettings { public bool EnableCache { get; set; } } // Program.cs var builder = Host.CreateApplicationBuilder(args); // Configuration is automatically loaded from: // 1. appsettings.json // 2. appsettings.{Environment}.json // 3. Environment variables // 4. Command-line arguments // Access configuration directly var connString = builder.Configuration["Database:ConnectionString"]; var maxRetries = builder.Configuration.GetValue("Database:MaxRetries"); // Bind to strongly-typed objects (Options pattern) builder.Services.Configure( builder.Configuration.GetSection("Database")); builder.Services.Configure( builder.Configuration.GetSection("Features")); var host = builder.Build(); // Use in services via IOptions public class DataService { private readonly DatabaseSettings _settings; public DataService(IOptions options) { _settings = options.Value; Console.WriteLine($"Connecting to: {_settings.ConnectionString}"); Console.WriteLine($"Max retries: {_settings.MaxRetries}"); } } // Override via environment variables: Database__ConnectionString=Server=prod // Override via command-line: --Database:MaxRetries=5 ``` ## Logging in .NET The `Microsoft.Extensions.Logging` API provides structured logging with support for multiple providers, log levels, and categories. ```csharp using Microsoft.Extensions.Logging; using Microsoft.Extensions.Hosting; var builder = Host.CreateApplicationBuilder(args); // Configure logging builder.Logging.ClearProviders(); builder.Logging.AddConsole(); builder.Logging.AddDebug(); builder.Logging.SetMinimumLevel(LogLevel.Information); // Filter by category builder.Logging.AddFilter("Microsoft", LogLevel.Warning); builder.Logging.AddFilter("MyApp.DataService", LogLevel.Debug); var host = builder.Build(); // Using ILogger in a service public class OrderService { private readonly ILogger _logger; public OrderService(ILogger logger) { _logger = logger; } public async Task ProcessOrderAsync(int orderId) { // Structured logging with message templates _logger.LogInformation("Processing order {OrderId}", orderId); try { var order = await FetchOrderAsync(orderId); // Log at different levels _logger.LogDebug("Order details: {Order}", order); if (order.Total > 10000) { _logger.LogWarning("Large order detected: {OrderId}, Total: {Total}", orderId, order.Total); } // Use scopes for correlated logs using (_logger.BeginScope("OrderId: {OrderId}", orderId)) { _logger.LogInformation("Validating order"); _logger.LogInformation("Charging payment"); _logger.LogInformation("Order completed"); } return order; } catch (Exception ex) { _logger.LogError(ex, "Failed to process order {OrderId}", orderId); throw; } } } // High-performance logging with source generators public static partial class LogMessages { [LoggerMessage(Level = LogLevel.Information, Message = "Processing order {orderId}")] public static partial void LogProcessingOrder(ILogger logger, int orderId); } ``` ## IHttpClientFactory `IHttpClientFactory` manages `HttpClient` instances with proper lifetime management, avoiding socket exhaustion and DNS issues common with manual `HttpClient` creation. ```csharp using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System.Net.Http.Json; public record Todo(int UserId, int Id, string Title, bool Completed); // Basic usage with IHttpClientFactory public class BasicTodoService { private readonly IHttpClientFactory _clientFactory; public BasicTodoService(IHttpClientFactory clientFactory) { _clientFactory = clientFactory; } public async Task GetTodosAsync() { var client = _clientFactory.CreateClient(); return await client.GetFromJsonAsync( "https://jsonplaceholder.typicode.com/todos") ?? []; } } // Typed client approach (recommended) public class TypedTodoService { private readonly HttpClient _client; public TypedTodoService(HttpClient client) { _client = client; _client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/"); _client.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0"); } public async Task GetUserTodosAsync(int userId) { return await _client.GetFromJsonAsync($"todos?userId={userId}") ?? []; } public async Task CreateTodoAsync(Todo todo) { var response = await _client.PostAsJsonAsync("todos", todo); response.EnsureSuccessStatusCode(); return await response.Content.ReadFromJsonAsync() ?? throw new InvalidOperationException("Failed to create todo"); } } // Program.cs - Registration var builder = Host.CreateApplicationBuilder(args); // Basic registration builder.Services.AddHttpClient(); // Named client builder.Services.AddHttpClient("github", client => { client.BaseAddress = new Uri("https://api.github.com/"); client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); client.DefaultRequestHeaders.Add("User-Agent", "MyApp"); }); // Typed client builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/"); client.Timeout = TimeSpan.FromSeconds(30); }); var host = builder.Build(); ``` ## Pattern Matching C# pattern matching enables concise conditional logic using `is` expressions and `switch` expressions with declaration, type, constant, relational, property, and list patterns. ```csharp public class PatternMatchingExamples { // Type and declaration patterns public string DescribeObject(object obj) => obj switch { null => "null value", string s => $"String of length {s.Length}", int n when n < 0 => $"Negative number: {n}", int n => $"Non-negative number: {n}", IEnumerable nums => $"Collection with {nums.Count()} items", _ => $"Unknown type: {obj.GetType().Name}" }; // Property patterns public record Person(string Name, int Age, Address? Address); public record Address(string City, string Country); public decimal CalculateDiscount(Person person) => person switch { { Age: < 18 } => 0.5m, // Under 18 { Age: >= 65 } => 0.3m, // Senior { Address.Country: "US", Age: >= 21 } => 0.1m, // US adult { Address: null } => 0m, // No address _ => 0.05m }; // Relational and logical patterns public string GetTemperatureDescription(int temp) => temp switch { < 0 => "Freezing", >= 0 and < 10 => "Cold", >= 10 and < 20 => "Cool", >= 20 and < 30 => "Warm", >= 30 => "Hot" }; // List patterns (C# 11+) public string AnalyzeSequence(int[] numbers) => numbers switch { [] => "Empty array", [var single] => $"Single element: {single}", [var first, var second] => $"Two elements: {first}, {second}", [var first, .., var last] => $"First: {first}, Last: {last}", [_, _, _, ..] => "Three or more elements" }; // Using 'is' with patterns public void ProcessValue(object value) { if (value is string { Length: > 0 } text) { Console.WriteLine($"Non-empty string: {text}"); } if (value is int num and > 0 and < 100) { Console.WriteLine($"Number between 1-99: {num}"); } if (value is not null) { Console.WriteLine($"Value is not null: {value}"); } } } ``` ## .NET CLI Commands The `dotnet` command is the generic driver for the .NET CLI, used for creating projects, building, running, testing, and publishing .NET applications. ```bash # Display .NET SDK version and information dotnet --version dotnet --info dotnet --list-sdks dotnet --list-runtimes # Create new projects dotnet new console -n MyApp # Console application dotnet new webapi -n MyApi # Web API dotnet new classlib -n MyLibrary # Class library dotnet new xunit -n MyTests # xUnit test project dotnet new sln -n MySolution # Solution file # Project management dotnet sln add MyApp/MyApp.csproj # Add project to solution dotnet add reference ../MyLibrary # Add project reference dotnet add package Newtonsoft.Json # Add NuGet package dotnet remove package Newtonsoft.Json # Remove NuGet package dotnet list package # List packages # Build and run dotnet restore # Restore dependencies dotnet build # Build the project dotnet build -c Release # Build in Release mode dotnet run # Build and run dotnet run -- arg1 arg2 # Pass arguments to app dotnet watch run # Run with hot reload # Testing dotnet test # Run tests dotnet test --filter "Category=Unit" # Filter tests dotnet test --collect:"XPlat Code Coverage" # With coverage # Publishing dotnet publish -c Release # Publish for deployment dotnet publish -r win-x64 --self-contained # Self-contained exe dotnet publish -r linux-x64 -p:PublishSingleFile=true # Single file # Running applications dotnet MyApp.dll # Run a published app dotnet exec MyApp.dll # Explicit execution ``` ## Records and Immutability Records provide concise syntax for creating immutable reference types with value-based equality, built-in deconstruction, and non-destructive mutation via `with` expressions. ```csharp // Record declaration with positional parameters public record Person(string FirstName, string LastName, int Age); // Record with additional members public record Employee(string FirstName, string LastName, int Age, string Department) : Person(FirstName, LastName, Age) { public string FullName => $"{FirstName} {LastName}"; public bool IsManager { get; init; } } // Record struct (value type, C# 10+) public readonly record struct Point(double X, double Y); public class RecordExamples { public void DemonstrateRecords() { // Creation var person1 = new Person("John", "Doe", 30); var person2 = new Person("John", "Doe", 30); // Value-based equality Console.WriteLine(person1 == person2); // True Console.WriteLine(person1.Equals(person2)); // True // Non-destructive mutation with 'with' var person3 = person1 with { Age = 31 }; Console.WriteLine(person3); // Person { FirstName = John, LastName = Doe, Age = 31 } // Deconstruction var (firstName, lastName, age) = person1; Console.WriteLine($"{firstName} {lastName} is {age}"); // ToString is auto-generated Console.WriteLine(person1); // Person { FirstName = John, LastName = Doe, Age = 30 } // Inheritance var employee = new Employee("Jane", "Smith", 28, "Engineering") { IsManager = true }; Console.WriteLine(employee.FullName); // Jane Smith } } ``` The .NET documentation repository provides comprehensive coverage of the entire .NET ecosystem, from language fundamentals in C#, F#, and Visual Basic to advanced topics like distributed systems with Orleans, machine learning with ML.NET, and IoT development. The documentation emphasizes practical examples, best practices, and integration patterns that help developers build robust, scalable applications. For developers working with .NET, this documentation serves as the authoritative reference for understanding language features, framework capabilities, and recommended patterns. The content is continuously updated to reflect new releases, with dedicated sections for what's new in each version and migration guides for upgrading existing applications. Whether building console apps, web APIs, desktop applications, or cloud-native microservices, the documentation provides the guidance needed to leverage .NET effectively.