C# In-Depth Explained: From Basics to Advanced – wiki词典

I have now completed the entire article.
“`markdown

C# In-Depth Explained: From Basics to Advanced

I. Introduction to C

C# (pronounced “C-sharp”) is a modern, object-oriented, and type-safe programming language developed by Microsoft as part of its .NET initiative. Designed to be a simple, powerful, and versatile language for building a wide range of applications, C# has evolved significantly since its inception, becoming a cornerstone of enterprise development, web applications, mobile apps, games, and cloud services.

A. What is C#?

  1. History and Evolution: Introduced in 2000 alongside the .NET Framework, C# was created by Anders Hejlsberg and his team at Microsoft. It draws inspiration from C++, Java, and Delphi, aiming to combine the power of C++ with the productivity of Java. Over the years, C# has consistently added new features, language constructs, and performance improvements, staying at the forefront of modern programming paradigms. Key milestones include the introduction of generics (C# 2.0), LINQ (C# 3.0), async/await (C# 5.0), and a continuous stream of enhancements in recent versions that align with the open-source, cross-platform .NET Core (now simply .NET).
  2. Key Features and Philosophy:
    • Object-Oriented: C# fully embraces OOP principles like encapsulation, inheritance, and polymorphism, facilitating modular and reusable code.
    • Type-safe: Strict type checking at compile-time and runtime helps prevent common programming errors, leading to more robust applications.
    • Component-oriented: Designed to build scalable and maintainable applications using software components, a concept central to the .NET ecosystem.
    • Garbage Collection: Automatic memory management frees developers from manual memory deallocation, reducing memory-related bugs.
    • Platform Independent (with .NET): While historically Windows-centric, modern C# with .NET can run on Windows, Linux, macOS, and more.
    • Rich Standard Library: Access to the extensive .NET Class Library provides a vast array of pre-built functionalities.

B. The .NET Ecosystem

C# is inextricably linked to the .NET platform, a free, open-source developer platform for building many different types of applications.

  1. .NET Runtime (CLR): The Common Language Runtime (CLR) is the virtual machine component of .NET. It handles the execution of C# code (and other .NET languages). Key CLR responsibilities include:
    • Just-In-Time (JIT) Compilation: Compiling C# Intermediate Language (IL) into native machine code at runtime.
    • Memory Management: Automatic garbage collection.
    • Security: Enforcing type safety and other security mechanisms.
    • Exception Handling: Providing a structured way to handle runtime errors.
  2. .NET Class Library (BCL): The Base Class Library is a comprehensive collection of reusable classes, interfaces, and value types that provide foundational functionality. It offers solutions for everything from file I/O and network communication to data structures, cryptography, and UI components, significantly accelerating development.
  3. Cross-platform capabilities (.NET Core / .NET 5+): The introduction of .NET Core (later renamed simply to .NET starting with .NET 5) marked a pivotal shift, making .NET truly cross-platform. Developers can now build and run C# applications on Windows, Linux, and macOS, fostering a broader reach and adoption of the language.

C. Setting Up Your Development Environment

To start coding in C#, you’ll need a suitable environment:

  1. Visual Studio / VS Code:
    • Visual Studio: Microsoft’s flagship Integrated Development Environment (IDE) for .NET. It’s a full-featured IDE offering powerful debugging, testing, and project management tools, primarily for Windows development but also available on macOS.
    • VS Code: A lightweight, cross-platform code editor that, with the C# Dev Kit extension, provides excellent support for C# development across all operating systems. It’s highly popular for its speed and extensibility.
  2. .NET SDK Installation: The .NET Software Development Kit (SDK) includes the .NET runtime, libraries, and command-line interface (CLI) tools necessary to build, run, and publish .NET applications. It can be downloaded from the official .NET website.

D. Your First C# Program (“Hello World”)

Let’s look at the classic “Hello World” program in C#:

“`csharp
using System;

namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(“Hello, World!”);
}
}
}
“`

  • using System;: Imports the System namespace, which contains fundamental classes like Console.
  • namespace HelloWorld: Organizes related code.
  • class Program: Defines a class, the basic building block for object-oriented programming.
  • static void Main(string[] args): This is the entry point of every C# application.
    • static: The method belongs to the class itself, not an instance of the class.
    • void: The method does not return any value.
    • Main: The conventional name for the entry point.
    • string[] args: An array to receive command-line arguments.
  • Console.WriteLine("Hello, World!");: Prints the string “Hello, World!” to the console.

With this foundation, we’re ready to delve deeper into the fundamental concepts of C# programming.

II. C# Fundamentals: The Building Blocks

At its core, C# provides a robust set of fundamental constructs for structuring code, handling data, and controlling program flow. Understanding these basics is crucial for writing any C# application.

A. Basic Syntax and Structure

  1. Statements, Blocks, and Comments:
    • Statements: Individual instructions in C# typically end with a semicolon (;).
      csharp
      int age = 30;
      Console.WriteLine("Hello");
    • Blocks: Code enclosed in curly braces {} groups statements together, forming method bodies, loop bodies, or conditional blocks.
    • Comments: Used to explain code.
      • Single-line: // This is a single-line comment
      • Multi-line: /* This is a multi-line comment */
      • XML Documentation: /// <summary>This is for XML documentation</summary>
  2. Namespaces: A way to organize code and prevent naming conflicts. The using directive allows you to use types defined in a namespace without fully qualifying them.
    “`csharp
    using System; // Allows using Console without System.Console

    namespace MyApplication
    {
    class Program
    {
    static void Main(string[] args)
    {
    Console.WriteLine(“Organized code.”);
    }
    }
    }
    ``
    3. **
    Main` Method**: As discussed, this is the entry point of a console application.

B. Data Types

C# is a strongly typed language, meaning every variable must have a defined type. Data types categorize data, dictating what kind of values a variable can hold and what operations can be performed on it.

  1. Value Types: Store their data directly in their own memory allocation.
    • Integral Numeric Types: sbyte, byte, short, ushort, int, uint, long, ulong, nint, nuint (e.g., int for integers).
    • Floating-Point Numeric Types: float, double, decimal (e.g., double for general-purpose floating-point numbers, decimal for financial calculations).
    • Boolean: bool (true/false).
    • Character: char (single Unicode character).
    • Structs: User-defined value types (e.g., DateTime).
    • Enums: Sets of named integral constants.
  2. Reference Types: Store a reference to the data, which is located elsewhere in memory (on the heap).
    • String: string (immutable sequence of characters).
    • Classes: User-defined reference types.
    • Interfaces: Contracts defining behavior.
    • Delegates: Type-safe function pointers.
    • Arrays: Collections of elements of the same type.
  3. Nullable Types (? operator): Allows value types to also hold a null value.
    csharp
    int? nullableInt = null;
    double? nullableDouble = 3.14;

C. Variables and Constants

  1. Declaration and Initialization: Variables must be declared with a type and can be initialized at the same time.
    csharp
    int count; // Declaration
    count = 10; // Initialization
    string name = "Alice"; // Declaration and Initialization
  2. var keyword (Implicitly Typed Local Variables): Introduced in C# 3.0, var infers the type of a local variable from its initialization expression. The type is still strongly determined at compile time.
    csharp
    var number = 100; // number is inferred as int
    var message = "Hello"; // message is inferred as string
    // var cannot be used for fields or return types.
  3. const and readonly:
    • const: For compile-time constants. Must be initialized at declaration and its value cannot be changed.
      csharp
      const double PI = 3.14159;
    • readonly: For runtime constants. Can be initialized at declaration or in the constructor, and its value cannot be changed afterwards. Typically used for fields.
      “`csharp
      class MyClass
      {
      public readonly int MaxValue = 100;
      public readonly string Id;

      public MyClass(string id)
      {
          Id = id; // Can be initialized in constructor
      }
      

      }
      “`

D. Operators

Operators perform operations on one or more operands.

  1. Arithmetic: +, -, *, /, % (modulus).
  2. Assignment: =, +=, -=, *=, /=, %=.
  3. Comparison (Relational): ==, !=, <, >, <=, >=. Return bool values.
  4. Logical: && (AND), || (OR), ! (NOT). Operate on bool expressions.
  5. Bitwise: &, |, ^, ~, <<, >>. Operate on integral types at the bit level.
  6. Ternary (Conditional): condition ? expression_if_true : expression_if_false. A shorthand for if-else.
    csharp
    string status = (age >= 18) ? "Adult" : "Minor";

E. Control Flow

Control flow statements dictate the order in which instructions are executed.

  1. Conditional Statements:
    • if-else: Executes different blocks of code based on a boolean condition.
      csharp
      if (temperature > 25)
      {
      Console.WriteLine("It's hot!");
      }
      else if (temperature < 10)
      {
      Console.WriteLine("It's cold!");
      }
      else
      {
      Console.WriteLine("It's moderate.");
      }
    • switch: Provides a way to choose between multiple execution paths based on the value of a single variable or expression.
      csharp
      char grade = 'B';
      switch (grade)
      {
      case 'A':
      Console.WriteLine("Excellent!");
      break;
      case 'B':
      case 'C': // Fall-through for multiple cases
      Console.WriteLine("Good.");
      break;
      default:
      Console.WriteLine("Needs improvement.");
      break;
      }
  2. Looping Constructs: Repeat a block of code multiple times.
    • for: Executes a block a specific number of times.
      csharp
      for (int i = 0; i < 5; i++)
      {
      Console.WriteLine($"Loop iteration: {i}");
      }
    • foreach: Iterates over elements in a collection.
      csharp
      string[] names = { "Alice", "Bob", "Charlie" };
      foreach (string name in names)
      {
      Console.WriteLine($"Hello, {name}!");
      }
    • while: Executes a block as long as a condition is true.
      csharp
      int count = 0;
      while (count < 3)
      {
      Console.WriteLine($"Count is {count}");
      count++;
      }
    • do-while: Executes a block at least once, then continues as long as a condition is true.
      carp
      int num = 0;
      do
      {
      Console.WriteLine($"Number is {num}");
      num++;
      } while (num < 0); // Will print "Number is 0" once
  3. Jump Statements: Alter the normal flow of execution.
    • break: Exits the innermost loop or switch statement.
    • continue: Skips the rest of the current loop iteration and proceeds to the next.
    • return: Exits the current method and returns a value (if the method has a non-void return type).
    • goto: Transfers control unconditionally to a labeled statement (generally discouraged for complex logic).

F. Methods (Functions)

Methods are blocks of code that perform a specific task. They promote code reusability and modularity.

  1. Defining and Calling Methods:
    “`csharp
    class Calculator
    {
    // Method definition
    public int Add(int a, int b)
    {
    return a + b;
    }

    static void Main(string[] args)
    {
        Calculator calc = new Calculator();
        int result = calc.Add(5, 3); // Method call
        Console.WriteLine($"Sum: {result}");
    }
    

    }
    2. **Parameters (Value, Reference (`ref`), Output (`out`), Parameter Arrays (`params`))**:
    * **Value Parameters (default)**: A copy of the argument's value is passed. Changes to the parameter inside the method do not affect the original argument.
    * **`ref` Parameters**: Passes the argument by reference. Changes to the parameter inside the method *do* affect the original argument. The argument must be initialized before passing.
    csharp
    void Increment(ref int num) { num++; }
    int x = 5;
    Increment(ref x); // x is now 6
    * **`out` Parameters**: Passes the argument by reference, but the method *must* assign a value to the parameter before it returns. The argument does not need to be initialized before passing.csharp
    void GetValue(out int value) { value = 10; }
    int y;
    GetValue(out y); // y is now 10
    * **`params` Parameters**: Allows a method to accept a variable number of arguments of a specific type. It must be the last parameter in the method signature.csharp
    public int Sum(params int[] numbers)
    {
    int total = 0;
    foreach (int n in numbers) { total += n; }
    return total;
    }
    // Usage: Sum(1, 2, 3) or Sum(new int[] {4, 5})
    3. **Method Overloading**: Defining multiple methods in the same class with the same name but different parameter lists (number, type, or order of parameters).csharp
    public int Add(int a, int b) { return a + b; }
    public double Add(double a, double b) { return a + b; }
    public int Add(int a, int b, int c) { return a + b + c; }
    4. **Expression-bodied members**: A concise syntax for methods (and properties, operators, etc.) that consist of a single expression.csharp
    public int Multiply(int a, int b) => a * b;
    “`

III. Object-Oriented Programming (OOP) in C

Object-Oriented Programming (OOP) is a programming paradigm based on the concept of “objects,” which can contain data and code: data in the form of fields (often known as attributes or properties), and code in the form of procedures (often known as methods). C# is a fully object-oriented language, and understanding its OOP features is fundamental to building complex, maintainable, and scalable applications.

A. Core OOP Concepts

  1. Encapsulation: The bundling of data (attributes) and methods (functions) that operate on the data into a single unit, or class, and restricting direct access to some of an object’s components. This protects the integrity of the data.
    • Classes and Objects:

      • Class: A blueprint or template for creating objects. It defines the structure and behavior that objects of that class will have.
      • Object: An instance of a class. You create objects from classes.
        “`csharp
        public class Car // Class
        {
        // Fields (data)
        public string Model { get; set; }
        public int Year { get; set; }

        // Method (behavior)
        public void StartEngine()
        {
        Console.WriteLine($”{Model} engine started.”);
        }
        }

      // Creating an object (instance) of the Car class
      Car myCar = new Car();
      myCar.Model = “Tesla Model 3”;
      myCar.Year = 2023;
      myCar.StartEngine();
      * **Access Modifiers**: Keywords that set the accessibility level of types and type members.
      * `public`: Accessible from anywhere.
      * `private`: Accessible only within the defining class or struct. (Default for class members).
      * `protected`: Accessible within the defining class and by derived class instances.
      * ``internal`: Accessible only within the same assembly.
      * `protected internal`: Accessible within the same assembly AND by derived class instances in other assemblies.
      * `private protected`: Accessible within the same class OR by derived class instances in the same assembly.
      2. **Constructors and Destructors**:
      * **Constructors**: Special methods used to initialize objects of a class. They have the same name as the class and no return type. Can be overloaded.
      csharp
      public class Person
      {
      public string Name { get; set; }
      public int Age { get; set; }

      // Default constructor
      public Person() { }
      
      // Parameterized constructor
      public Person(string name, int age)
      {
          Name = name;
          Age = age;
      }
      

      }
      Person p1 = new Person(); // Uses default constructor
      Person p2 = new Person(“Alice”, 30); // Uses parameterized constructor
      * **Destructors**: Rarely used in modern C# due to automatic garbage collection. They perform cleanup operations before an object is garbage-collected.carp
      ~MyClass() // Destructor
      {
      // Cleanup code (e.g., releasing unmanaged resources)
      }
      3. **Properties (Getters and Setters, Auto-Implemented Properties)**: Provide a flexible mechanism to read, write, or compute the value of a private field. They encapsulate access to data.
      * **Full Property**:
      csharp
      private string _firstName; // Private backing field
      public string FirstName
      {
      get { return _firstName; }
      set
      {
      if (string.IsNullOrWhiteSpace(value))
      throw new ArgumentException(“Name cannot be empty.”);
      _firstName = value;
      }
      }
      * **Auto-Implemented Property**: A shorthand when no additional logic is needed in the getter or setter. The compiler automatically creates a private backing field.csharp
      public string LastName { get; set; } // Auto-implemented property
      ``
      4. **Fields vs. Properties**:
      * **Fields**: Variables directly declared within a class or struct. They represent the data contained within an object. Typically kept
      privateto enforce encapsulation.
      * **Properties**: Members that provide a flexible way to read, write, or compute the value of a private field. They are often
      public` and provide controlled access to the internal state of an object.

B. Inheritance

Inheritance allows a class (derived class or subclass) to inherit fields and methods from another class (base class or superclass), promoting code reuse and establishing an “is-a” relationship. C# supports single inheritance for classes.

  1. Base and Derived Classes:
    “`csharp
    public class Animal // Base class
    {
    public string Species { get; set; }
    public void Eat() { Console.WriteLine(“Animal is eating.”); }
    }

    public class Dog : Animal // Derived class, inherits from Animal
    {
    public string Breed { get; set; }
    public void Bark() { Console.WriteLine(“Woof!”); }
    }

    Dog myDog = new Dog();
    myDog.Species = “Canine”; // Inherited property
    myDog.Eat(); // Inherited method
    myDog.Breed = “Golden Retriever”;
    myDog.Bark();
    2. **`base` keyword**: Used to access members of the base class from within a derived class, particularly useful for calling base class constructors or overridden methods.csharp
    public class Vehicle
    {
    public string Make { get; set; }
    public Vehicle(string make) { Make = make; }
    }

    public class Car : Vehicle
    {
    public int NumberOfDoors { get; set; }
    public Car(string make, int doors) : base(make) // Call base constructor
    {
    NumberOfDoors = doors;
    }
    }
    3. **Method Overriding (`virtual`, `override`, `new`)**:
    * **`virtual`**: A method in the base class marked `virtual` can be overridden by a derived class.
    * **`override`**: A method in the derived class marked `override` provides a new implementation for a `virtual` method inherited from its base class.
    csharp
    public class Shape
    {
    public virtual void Draw() { Console.WriteLine(“Drawing a generic shape.”); }
    }

    public class Circle : Shape
    {
        public override void Draw() { Console.WriteLine("Drawing a circle."); }
    }
    ```
    
    • new: Hides an inherited member from the base class. It does not override; it creates a new member with the same name. Generally, override is preferred for polymorphic behavior.
    • sealed Classes and Methods:
    • sealed class: Cannot be inherited by any other class. Useful for preventing further specialization.
    • sealed method: An override method can be marked sealed to prevent further overriding in subsequent derived classes.

C. Polymorphism

Polymorphism (meaning “many forms”) allows objects of different classes to be treated as objects of a common base type. This is achieved through method overriding and interfaces.

  1. Compile-time (Method Overloading): Achieved by defining multiple methods with the same name but different parameter lists within the same class. The compiler determines which method to call based on the arguments. (Discussed in “C# Fundamentals” section).
  2. Run-time (Method Overriding, Interfaces):
    • Method Overriding: When a derived class provides its own implementation of a method that is already defined in its base class (marked virtual in base, override in derived). The actual method called depends on the object’s runtime type.
      csharp
      Shape s = new Circle(); // s is declared as Shape, but points to a Circle object
      s.Draw(); // Calls Circle's Draw() method (runtime polymorphism)
    • Interfaces: Define a contract that classes can implement. A variable of an interface type can refer to any object that implements that interface, allowing for polymorphic behavior across unrelated class hierarchies.

D. Abstraction

Abstraction involves showing only essential information and hiding the complex implementation details. In C#, this is achieved using abstract classes and interfaces.

  1. Abstract Classes and Abstract Methods:
    • Abstract Class: A class that cannot be instantiated on its own and may contain abstract (unimplemented) members. It acts as a base class for other classes.
    • Abstract Method: A method declared in an abstract class without an implementation. Derived classes must implement all abstract methods.
      “`csharp
      public abstract class Payment
      {
      public abstract void ProcessPayment(decimal amount); // Abstract method

      public void LogTransaction(decimal amount) // Concrete method
      {
          Console.WriteLine($"Transaction logged for {amount:C}");
      }
      

      }

      public class CreditCardPayment : Payment
      {
      public override void ProcessPayment(decimal amount)
      {
      Console.WriteLine($”Processing credit card payment of {amount:C}”);
      }
      }
      * **Interfaces (Defining Contracts, Default Interface Methods in C# 8+)**:
      * **Interfaces**: Define a contract that specifies a set of public methods, properties, events, or indexers that implementing classes or structs must provide. They cannot contain fields or constructors.
      csharp
      public interface ILogger
      {
      void LogMessage(string message);
      }

      public class ConsoleLogger : ILogger
      {
      public void LogMessage(string message)
      {
      Console.WriteLine($”LOG: {message}”);
      }
      }
      * **Default Interface Methods (C# 8+)**: Allow an interface to provide a default implementation for a method, which can be used by implementing classes or overridden. This helps in evolving interfaces without breaking existing implementations.csharp
      public interface IService
      {
      void Start();
      // Default implementation
      void Stop() { Console.WriteLine(“Service stopped gracefully.”); }
      }
      “`

E. Structs vs. Classes

  • Classes: Reference types. Stored on the heap. When assigned, only the reference is copied. Support inheritance and polymorphism. Generally used for larger, more complex objects that require object identity.
  • Structs: Value types. Stored on the stack (or inline in arrays/objects). When assigned, the entire value is copied. Do not support inheritance (except implicitly from System.ValueType). Generally used for small data structures that represent single values, where copying by value is desired, and for performance-critical scenarios.

F. Enums

Enumerations (enum) provide a way to define a set of named integral constants. They improve code readability and prevent errors from using arbitrary numbers.

“`csharp
public enum DayOfWeek
{
Sunday, // 0 by default
Monday, // 1
Tuesday, // 2
Wednesday, // 3
Thursday, // 4
Friday, // 5
Saturday // 6
}

public enum StatusCode : byte // Specify underlying type
{
Success = 200,
NotFound = 404,
InternalError = 500
}

DayOfWeek today = DayOfWeek.Wednesday;
Console.WriteLine($”Today is {today}. Its value is {(int)today}”); // Output: Today is Wednesday. Its value is 3
“`

IV. Advanced C# Features

Beyond the fundamentals and core OOP principles, C# offers a rich set of advanced features that empower developers to write more efficient, expressive, and robust code. These features often address complex programming challenges and leverage the full power of the .NET ecosystem.

A. Generics

Generics allow you to design classes, interfaces, and methods that operate on types specified by the client code, without committing to specific actual types. This provides type safety without sacrificing performance or code reusability.

  1. Generic Classes, Interfaces, and Methods:
    “`csharp
    // Generic Class
    public class MyGenericList
    {
    private T[] _items;
    private int _count;

    public MyGenericList(int capacity)
    {
        _items = new T[capacity];
        _count = 0;
    }
    
    public void Add(T item)
    {
        if (_count < _items.Length)
        {
            _items[_count++] = item;
        }
    }
    public T Get(int index) => _items[index];
    

    }

    // Generic Method
    public class Utilities
    {
    public static void Swap(ref T a, ref T b)
    {
    T temp = a;
    a = b;
    b = temp;
    }
    }

    MyGenericList intList = new MyGenericList(10);
    intList.Add(1);
    int intVal1 = 5, intVal2 = 10;
    Utilities.Swap(ref intVal1, ref intVal2); // intVal1 is now 10, intVal2 is 5
    2. **Type Constraints (`where` clause)**: You can restrict the types that can be used for a type parameter using constraints.csharp
    public class MyRestrictedList where T : IComparable, new() // T must implement IComparable and have a parameterless constructor
    {
    // …
    }
    ``
    3. **Benefits of Generics**:
    * **Type Safety**: Catches type mismatches at compile time, reducing runtime errors.
    * **Code Reusability**: Write a single generic class/method that works with various types.
    * **Performance**: Avoids boxing/unboxing overhead that occurs with non-generic collections storing
    object`.

B. Delegates and Events

Delegates and events are fundamental to building loosely coupled, extensible systems, especially for event-driven programming.

  1. Delegates (Type-safe Function Pointers): A delegate is a type that represents references to methods with a particular parameter list and return type. They are often described as type-safe function pointers.
    “`csharp
    public delegate void MyDelegate(string message); // Declare a delegate type

    public class Notifier
    {
    public void SendNotification(string msg)
    {
    Console.WriteLine($”Notification: {msg}”);
    }
    }

    // Usage
    Notifier notifier = new Notifier();
    MyDelegate handler = notifier.SendNotification; // Assign method to delegate
    handler(“Hello from delegate!”);
    2. **Events (Publisher-Subscriber Model)**: Events provide a way for a class to notify other classes or objects when something of interest happens. They are built on delegates and enforce a publisher-subscriber model.csharp
    public class Button
    {
    // Declare an event using the EventHandler delegate
    public event EventHandler Click;

    public void SimulateClick()
    {
        // Raise the event
        Click?.Invoke(this, EventArgs.Empty);
    }
    

    }

    public class Subscriber
    {
    public Subscriber(Button button)
    {
    button.Click += Button_Click; // Subscribe to the event
    }

    private void Button_Click(object sender, EventArgs e)
    {
        Console.WriteLine("Button was clicked!");
    }
    

    }

    Button btn = new Button();
    Subscriber sub = new Subscriber(btn);
    btn.SimulateClick(); // Output: Button was clicked!
    3. **Lambda Expressions and Anonymous Methods**: Concise ways to create anonymous functions (methods without a name) that can be assigned to delegate types or used in LINQ queries.csharp
    // Anonymous method
    MyDelegate anonHandler = delegate(string msg) { Console.WriteLine($”Anon: {msg}”); };
    anonHandler(“Anonymous message”);

    // Lambda expression
    MyDelegate lambdaHandler = msg => Console.WriteLine($”Lambda: {msg}”);
    lambdaHandler(“Lambda message”);

    // Lambda for LINQ
    List numbers = new List { 1, 2, 3, 4, 5 };
    var evenNumbers = numbers.Where(n => n % 2 == 0); // n => n % 2 == 0 is a lambda
    “`

C. Language Integrated Query (LINQ)

LINQ (Language Integrated Query) is a powerful set of features introduced in C# 3.0 that provides a uniform query syntax for data from various sources (objects, databases, XML, etc.).

  1. LINQ to Objects, LINQ to XML, LINQ to SQL/Entities:
    • LINQ to Objects: Queries against in-memory collections (List<T>, arrays).
    • LINQ to XML: Queries against XML documents.
    • LINQ to SQL/Entities: Queries against relational databases (often through Entity Framework).
  2. Query Syntax vs. Method Syntax: LINQ offers two syntaxes.
    • Query Syntax (SQL-like):
      csharp
      List<string> names = new List<string> { "Alice", "Bob", "Charlie", "David" };
      var longNames = from name in names
      where name.Length > 4
      orderby name ascending
      select name;
    • Method Syntax (Extension Methods): More flexible, often preferred for complex queries.
      csharp
      var longNamesMethod = names.Where(name => name.Length > 4)
      .OrderBy(name => name)
      .Select(name => name);
  3. Common LINQ Operators: Where, Select, OrderBy, GroupBy, Join, Distinct, Any, All, Count, Sum, Average, etc.

D. Asynchronous Programming (async and await)

Asynchronous programming allows applications to remain responsive by executing long-running operations without blocking the main thread. C#’s async/await keywords simplify this process significantly.

  1. Understanding Asynchrony: Traditional synchronous code executes line by line. Asynchronous code allows a program to start a long-running task (e.g., fetching data from a web server) and continue with other work while waiting for that task to complete.
  2. Task Parallel Library (TPL): The Task and Task<TResult> classes are central to asynchronous programming in .NET, representing an asynchronous operation that can be waited on.
  3. async and await:

    • async: Modifies a method declaration, indicating that the method contains await expressions and can execute asynchronously.
    • await: Pauses the execution of the async method until the awaited Task completes, without blocking the calling thread. Control returns to the caller.
      “`csharp
      public async Task DownloadContentAsync(string url)
      {
      using (HttpClient client = new HttpClient())
      {
      string content = await client.GetStringAsync(url); // await pauses here
      return content;
      }
      }

    public async Task CallDownload()
    {
    Console.WriteLine(“Starting download…”);
    string data = await DownloadContentAsync(“https://example.com”); // await pauses here
    Console.WriteLine(“Download complete. Content length: ” + data.Length);
    }
    ``
    4. **Error Handling in Async Methods**: Use
    try-catchblocks as usual. Exceptions thrown inasyncmethods are captured by theTaskand re-thrown when theTask` is awaited.

E. Exception Handling

Structured exception handling provides a robust way to manage errors that occur during program execution, preventing application crashes.

  1. try-catch-finally blocks:
    • try: Contains the code that might throw an exception.
    • catch: Handles the exception if one occurs. You can have multiple catch blocks for different exception types.
    • finally: Contains code that is guaranteed to execute, regardless of whether an exception occurred or was handled (e.g., resource cleanup).
      csharp
      try
      {
      int result = 10 / int.Parse("0"); // This will throw a DivideByZeroException
      }
      catch (DivideByZeroException ex)
      {
      Console.WriteLine($"Cannot divide by zero: {ex.Message}");
      }
      catch (FormatException ex)
      {
      Console.WriteLine($"Invalid number format: {ex.Message}");
      }
      catch (Exception ex) // General catch block (should be last)
      {
      Console.WriteLine($"An unexpected error occurred: {ex.Message}");
      }
      finally
      {
      Console.WriteLine("Execution finished (finally block).");
      }
  2. Custom Exceptions: You can define your own exception classes by deriving from Exception or one of its subclasses.
    csharp
    public class InvalidInputException : Exception
    {
    public InvalidInputException(string message) : base(message) { }
    }
  3. using statement and IDisposable: The using statement ensures that an object implementing the IDisposable interface is correctly disposed of (e.g., file streams, database connections), even if an exception occurs.
    csharp
    using (StreamReader reader = new StreamReader("file.txt"))
    {
    string line = reader.ReadLine();
    Console.WriteLine(line);
    } // reader.Dispose() is automatically called here

F. Reflection

Reflection is the ability of a program to examine and modify its own structure and behavior at runtime. It allows you to inspect metadata about types, members, and assemblies.

  1. Inspecting Metadata at Runtime:
    “`csharp
    Type type = typeof(string);
    Console.WriteLine($”Type Name: {type.Name}”);
    foreach (var method in type.GetMethods())
    {
    // Console.WriteLine($” Method: {method.Name}”); // This would print many methods
    }

    object str = “Hello”;
    Type dynamicType = str.GetType(); // Get type of object at runtime
    “`
    2. Dynamic Type Creation: More advanced uses allow for creating types and invoking methods dynamically at runtime, often used in frameworks.

G. Attributes

Attributes are declarative tags that can be placed on code elements (assemblies, types, methods, properties, etc.) to associate metadata or behavioral instructions with them.

  1. Predefined Attributes: C# and .NET provide many built-in attributes (e.g., [Obsolete], [Serializable], [Conditional], [DllImport]).
    csharp
    [Obsolete("This method is deprecated. Use NewMethod() instead.")]
    public void OldMethod() { /* ... */ }
  2. Custom Attributes: You can define your own attributes by deriving from System.Attribute.
    “`csharp
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class AuthorAttribute : Attribute
    {
    public string Name { get; set; }
    public int Version { get; set; }
    public AuthorAttribute(string name) { Name = name; Version = 1; }
    }

    [Author(“John Doe”, Version = 2)]
    public class MyClass
    {
    // …
    }
    “`

H. Extension Methods

Extension methods allow you to “add” new methods to existing types without modifying their source code or creating a new derived type. They are static methods but are called as if they were instance methods on the extended type.

“`csharp
public static class StringExtensions
{
public static int WordCount(this string str) // ‘this’ keyword indicates an extension method
{
return str.Split(new char[] { ‘ ‘, ‘.’, ‘?’ }, StringSplitOptions.RemoveEmptyEntries).Length;
}
}

string sentence = “Hello world, how are you?”;
int count = sentence.WordCount(); // Calls the extension method as if it were an instance method
Console.WriteLine($”Word count: {count}”); // Output: 5
“`

I. Nullable Reference Types (C# 8+)

C# 8 introduced Nullable Reference Types (NRTs) to help mitigate NullReferenceExceptions, a common source of bugs. By default, reference types are non-nullable, and the compiler provides warnings if a non-nullable reference type is assigned null or if a nullable reference type is dereferenced without a null check.

“`csharp

nullable enable // Enable NRTs for the file

string name = null; // Warning: Converting null literal or possible null value to non-nullable type.
string? nullableName = null; // No warning

if (nullableName != null)
{
Console.WriteLine(nullableName.Length); // No warning because of null check
}

// Or using the null-forgiving operator (!)
string nonNullable = nullableName!; // Asserts that nullableName is not null (use with caution)

nullable disable // Disable NRTs for the file

“`

J. Pattern Matching (C# 7+ and beyond)

Pattern matching provides a more concise and expressive syntax for testing an expression to see if it has certain characteristics and, if it does, assigning variables from that expression.

``csharp
// C# 7:
is` operator with type pattern
public void PrintType(object o)
{
if (o is int i) // Checks if o is an int, and if so, assigns it to i
{
Console.WriteLine($”It’s an integer: {i}”);
}
else if (o is string s)
{
Console.WriteLine($”It’s a string: {s}”);
}
}

// C# 8: Switch expression
public string GetDayType(DayOfWeek day) => day switch
{
DayOfWeek.Saturday or DayOfWeek.Sunday => “Weekend”,
_ => “Weekday” // discard pattern for any other value
};

// C# 9: Relational patterns, logical patterns
public string GetTemperatureDescription(double temp) => temp switch
{
< 0 => “Freezing”,
>= 0 and < 15 => “Cold”,
>= 15 and < 25 => “Mild”,
>= 25 and <= 35 => “Warm”,
> 35 => “Hot”,
_ => “Unknown”
};
“`

K. Records (C# 9+)

Records are reference types that provide encapsulated data with value-based equality. They are designed for scenarios where you primarily want to store data and rely on immutability and value semantics.

“`csharp
// Record type declaration
public record Person(string FirstName, string LastName);

// Usage
Person p1 = new Person(“Jane”, “Doe”);
Person p2 = new Person(“Jane”, “Doe”);
Person p3 = new Person(“John”, “Doe”);

Console.WriteLine(p1 == p2); // Output: True (value-based equality)
Console.WriteLine(p1 == p3); // Output: False

// Immutable properties by default
// p1.FirstName = “Janet”; // Compile-time error

// With-expressions for non-destructive mutation
Person p4 = p1 with { FirstName = “Janet” };
Console.WriteLine(p4); // Output: Person { FirstName = Janet, LastName = Doe }
“`

V. .NET Ecosystem and Modern C# Development

Modern C# development is deeply intertwined with the broader .NET ecosystem, which provides a comprehensive framework, tools, and libraries for building diverse applications. The evolution of .NET (especially .NET Core and its successors) has made C# a truly cross-platform language suitable for a multitude of application types.

A. Web Development with ASP.NET Core

ASP.NET Core is a cross-platform, high-performance, open-source framework for building modern, cloud-enabled, internet-connected applications. It supports various development models for web applications and APIs.

  1. MVC (Model-View-Controller): A popular architectural pattern that separates an application into three main components:
    • Model: Represents the application’s data and business logic.
    • View: Displays the user interface.
    • Controller: Handles user input, interacts with the model, and selects a view to render.
      MVC is widely used for building robust web applications with a clear separation of concerns.
  2. Razor Pages: A simpler, page-focused alternative to MVC for building dynamic web UI. Each Razor Page combines C# code with HTML markup in a .cshtml file, making small, self-contained pages easier to develop.
  3. Web APIs: ASP.NET Core provides excellent support for building RESTful APIs, which are essential for single-page applications (SPAs), mobile backends, and microservices. Controllers handle HTTP requests and return data, often in JSON format.
  4. Blazor (WebAssembly and Server): A framework for building interactive client-side web UI with C# instead of JavaScript.
    • Blazor WebAssembly: Runs C# code directly in the browser on a WebAssembly-based .NET runtime.
    • Blazor Server: Runs C# code on the server, with UI updates handled over a SignalR connection.

B. Data Access with Entity Framework Core

Entity Framework Core (EF Core) is a modern, cross-platform, open-source object-relational mapper (ORM) for .NET applications. It enables developers to work with a database using .NET objects, eliminating the need for most of the data-access code that developers usually need to write.

  1. Code-First, Database-First:
    • Code-First: You define your data model using C# classes, and EF Core creates the database schema from your model. This is the most common approach in new projects.
    • Database-First: You generate your C# model classes from an existing database.
  2. Migrations: A feature in EF Core that allows you to manage changes to your database schema over time, as your model evolves. You create migration files that describe how to update the database to match your current model.
  3. LINQ to Entities: EF Core integrates seamlessly with LINQ, allowing you to write database queries using C# and LINQ syntax. EF Core translates these LINQ queries into SQL queries that are executed against the database.

C. Desktop Application Development

C# remains a strong choice for building desktop applications, offering several frameworks.

  1. WPF (Windows Presentation Foundation): A UI framework that creates rich desktop client applications for Windows. It uses XAML (Extensible Application Markup Language) for UI definitions and C# for business logic. WPF offers powerful data binding, styling, and templating capabilities.
  2. WinForms (Windows Forms): An older, but still used, UI framework for building Windows desktop applications. It’s event-driven and provides a drag-and-drop interface designer. Simpler for basic applications but less flexible than WPF for complex UIs.
  3. .NET MAUI (Multi-platform App UI): The evolution of Xamarin.Forms, .NET MAUI is a cross-platform framework for creating native mobile and desktop apps with C# and XAML. It allows developers to write a single codebase that can run on Android, iOS, macOS, and Windows, greatly accelerating cross-platform development.

D. Microservices Architecture

C# and .NET Core are exceptionally well-suited for building microservices-based applications. Microservices are small, independent services that communicate with each other, each running in its own process and deployable independently.

  1. Building and Deploying Microservices: ASP.NET Core provides lightweight and fast frameworks ideal for creating individual microservices. Features like built-in dependency injection, configuration management, and logging are crucial.
  2. Containerization (Docker): Docker (and other containerization technologies) are commonly used to package microservices and their dependencies into portable, isolated units. .NET applications can be easily containerized, enabling consistent deployment across different environments.

E. Cloud Integration

C# and .NET have strong support for cloud development across major cloud providers.

  1. Azure, AWS, Google Cloud Platform: .NET SDKs and tools are available for interacting with services on Microsoft Azure, Amazon Web Services (AWS), and Google Cloud Platform (GCP). This allows C# applications to leverage cloud storage, databases, messaging queues, machine learning, and more.
  2. Serverless Functions (Azure Functions, AWS Lambda): C# is a supported language for building serverless functions, enabling developers to write and deploy small, event-driven pieces of code that scale automatically and only incur costs when executed.

F. Unit Testing and Integration Testing

Robust testing is an integral part of modern software development, and C# provides excellent tools and frameworks for this.

  1. xUnit, NUnit, MSTest: These are the three most popular unit testing frameworks for .NET. They provide attributes to mark test methods, assertions to verify outcomes, and test runners to execute tests.
  2. Mocking Frameworks (Moq): Libraries like Moq allow you to create mock objects for dependencies (e.g., database access, external services) during unit testing. This isolates the code under test, making tests faster and more reliable.

VI. Best Practices and Design Patterns

Writing effective C# code goes beyond knowing the syntax and features; it involves adhering to best practices and leveraging established design patterns to create maintainable, scalable, and robust applications.

A. SOLID Principles

SOLID is an acronym for five design principles intended to make software designs more understandable, flexible, and maintainable.

  1. Single Responsibility Principle (SRP): A class should have only one reason to change. This means a class should only have one primary responsibility or job.
    • Bad Example: A Report class that also handles printing and saving.
    • Good Example: Separate ReportGenerator, ReportPrinter, and ReportSaver classes.
  2. Open/Closed Principle (OCP): Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. You should be able to extend a class’s behavior without altering its source code. Achieved through inheritance and interfaces.
  3. Liskov Substitution Principle (LSP): Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program. If class B is a subtype of class A, then A can be replaced by B without breaking the program.
    • Bad Example: A Rectangle class and a Square class where Square inherits from Rectangle, but setting Width on Square also sets Height, violating Rectangle behavior expectations.
  4. Interface Segregation Principle (ISP): Clients should not be forced to depend on interfaces they do not use. Instead of one large, general-purpose interface, create smaller, role-specific interfaces.
    • Bad Example: An ILargeWorker interface with Work, Eat, Sleep methods, even if a Robot only needs Work.
    • Good Example: IWorker, IEater, ISleeper interfaces.
  5. Dependency Inversion Principle (DIP):
    • High-level modules should not depend on low-level modules. Both should depend on abstractions.
    • Abstractions should not depend on details. Details should depend on abstractions.
      This principle often leads to the use of Dependency Injection.

B. Dependency Injection (DI)

Dependency Injection is a technique in which an object receives other objects that it depends on. These other objects are called dependencies. This promotes loose coupling and makes code easier to test and maintain.

  1. Inversion of Control (IoC) Containers: Frameworks like Microsoft.Extensions.DependencyInjection, Autofac, or Ninject manage the creation and lifetime of objects and inject their dependencies.
  2. Constructor, Property, and Method Injection:
    • Constructor Injection: Dependencies are provided through a class’s constructor. This is the most common and recommended approach as it ensures the object is always in a valid state.
    • Property Injection: Dependencies are set through public properties. Useful for optional dependencies.
    • Method Injection: Dependencies are passed as parameters to a method. Used when a dependency is only needed for a specific method call.

C. Common Design Patterns

Design patterns are reusable solutions to common problems in software design. They are not concrete implementations but rather templates for how to solve problems.

  1. Creational Patterns: Deal with object creation mechanisms, trying to create objects in a manner suitable to the situation.
    • Singleton: Ensures a class has only one instance and provides a global point of access to it.
    • Factory Method: Defines an interface for creating an object, but lets subclasses decide which class to instantiate.
  2. Structural Patterns: Deal with the composition of classes and objects.
    • Adapter: Allows incompatible interfaces to work together.
    • Decorator: Attaches additional responsibilities to an object dynamically.
  3. Behavioral Patterns: Deal with algorithms and the assignment of responsibilities between objects.
    • Strategy: Defines a family of algorithms, encapsulates each one, and makes them interchangeable.
    • Observer: Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

D. Code Quality and Maintainability

  1. Code Style and Formatting: Consistent code style (e.g., using EditorConfig, adhering to C# conventions) improves readability and makes collaboration easier.
  2. Refactoring Techniques: Regularly restructuring existing computer code without changing its external behavior, in order to improve non-functional attributes of the software (e.g., readability, complexity, maintainability).
  3. Documentation (XML Comments): Using XML documentation comments (///) allows developers to describe classes, methods, and other members, which can then be used by IDEs (like Visual Studio) for IntelliSense and to generate documentation.

csharp
/// <summary>
/// Represents a simple calculator.
/// </summary>
public class SimpleCalculator
{
/// <summary>
/// Adds two integers and returns the sum.
/// </summary>
/// <param name="a">The first integer.</param>
/// <param name="b">The second integer.</param>
/// <returns>The sum of <paramref name="a"/> and <paramref name="b"/>.</returns>
public int Add(int a, int b)
{
return a + b;
}
}

VII. Conclusion

C# has firmly established itself as a powerful, versatile, and continuously evolving programming language. From its strong foundations in object-oriented principles to its cutting-edge features and comprehensive ecosystem, C# empowers developers to build virtually any type of application imaginable.

A. Recap of C#’s Power and Versatility

We’ve journeyed through the core aspects of C#, starting with its fundamental syntax and data handling, progressing to the robust paradigms of Object-Oriented Programming (OOP) with classes, inheritance, and polymorphism. We then explored advanced features like generics for type-safe reusable code, LINQ for expressive data querying, and async/await for responsive applications. Finally, we touched upon the expansive .NET ecosystem, showcasing how C# seamlessly integrates with web development (ASP.NET Core, Blazor), data access (EF Core), desktop applications (.NET MAUI), microservices, and cloud computing. This demonstrates C#’s adaptability and suitability for diverse development needs.

B. Continuous Learning and Future Trends

The C# and .NET landscape is dynamic, with new versions released annually, bringing performance improvements, new language constructs, and expanded capabilities. To remain proficient, continuous learning is key. Keep an eye on:

  • Further language enhancements: C# continues to evolve with features like primary constructors, collection expressions, and more.
  • Performance improvements: Each .NET release typically brings significant performance gains.
  • Cloud-native development: C# and .NET’s role in serverless, containerized, and microservices architectures continues to grow.
  • AI/ML integration: Libraries like ML.NET are making it easier to integrate machine learning into C# applications.

C. Resources for Further Exploration

To deepen your understanding and continue your C# journey, consider these resources:

  • Official Microsoft Documentation: The definitive source for C# and .NET information (docs.microsoft.com).
  • Microsoft Learn: Free interactive learning paths and modules.
  • GitHub: Explore open-source C# projects and contribute to the community.
  • Online Courses and Tutorials: Platforms like Pluralsight, Udemy, Coursera, and freeCodeCamp offer extensive learning materials.
  • Community Forums and Blogs: Engage with the vibrant C# community to learn from others and share your knowledge.

C# offers a rewarding development experience, blending high productivity with powerful performance and a rich feature set. By mastering its principles and staying current with its advancements, you’ll be well-equipped to tackle complex challenges and build innovative software solutions. Happy coding!
“`

滚动至顶部