跳到主要内容

C# 进阶提升

一、方法 📝

C# 中,方法是执行特定任务的代码块,可以从程序的其他部分调用。方法可促进代码重用、可读性和模块化。

1、方法声明 📝

public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
}

基本组成部分:

  • 访问修饰符(如 public, private
  • 返回类型(如 void, int, string 等)
  • 方法名(使用驼峰命名法)
  • 参数列表(可选)

2、调用方法 🔗

实例方法调用

Calculator calculator = new Calculator();
int result = calculator.Add(1, 2);
Console.WriteLine(result);

静态方法调用

public class MathHelper
{
public static int Square(int x)
{
return x * x;
}
}

// Call directly using the class name
int result = MathHelper.Square(4); // result = 16

3、方法重载 🔄

  1. 方法名称必须相同
  2. 参数列表必须不同:参数数量不同、参数类型不同、参数顺序不同
public class Calculator
{
// Add two integers
public int Add(int a, int b)
{
return a + b;
}

// Add two double-precision floating-point numbers
public double Add(double a, double b)
{
return a + b;
}

// Add three integers
public int Add(int a, int b, int c)
{
return a + b + c;
}

// Different parameter order
public int Add(int a, string b)
{
return a + int.Parse(b);
}
}

4、方法参数 📌

必需参数

public class MathOperations
{
public int Multiply(int x, int y)
{
return x * y;
}
}

可选参数

可选参数提供了一种简洁的方法来处理具有默认行为的方法调用,其必须在必填参数之后。

public class Printer
{
// Optional parameter with a default value
public void PrintMessage(string message, bool uppercase = false)
{
if (uppercase)
{
Console.WriteLine(message.ToUpper());
}
else
{
Console.WriteLine(message);
}
}
}

值参数(默认)

void ModifyValue(int x)
{
x = 10; // Will not change the original value
}

引用参数(ref

void ModifyReference(ref int x)
{
x = 10; // Will change the original value
}

int number = 5;
ModifyReference(ref number); // number is now 10

输出参数(out

提供一种简洁的多返回值机制,特别适合需要同时返回状态和结果的场景。

public class UserValidator
{
// Validate user information
public bool ValidateUser(string input, out int userId, out string errorMessage)
{
userId = -1;
errorMessage = "";

// Parse user ID
if (!int.TryParse(input, out userId))
{
errorMessage = "Invalid user ID format";
return false;
}

// Check ID range
if (userId <= 0)
{
errorMessage = "User ID must be greater than 0";
return false;
}

return true;
}
}

public void ProcessUser()
{
UserValidator validator = new UserValidator();

// Call the method
bool isValid = validator.ValidateUser("123", out int userId, out string message);

if (isValid)
{
Console.WriteLine($"User ID: {userId}");
}
else
{
Console.WriteLine($"Validation failed: {message}");
}
}

参数数组(params

public int Sum(params int[] numbers)
{
int total = 0;
foreach(int num in numbers)
total += num;
return total;
}

5、返回类型 📤

有返回值的方法

public class Circle
{
public double CalculateArea(double radius)
{
return Math.PI * radius * radius;
}
}

空方法

public class Logger
{
public void LogError(string errorMessage)
{
Console.WriteLine($"Error: {errorMessage}");
}
}

二、面向对象编程 📦

1、原则 📝

面向对象编程基于四个主要原则:封装、继承、多态性和抽象。

  • 封装允许将数据和方法捆绑到一个单元(类)中并限制访问到某些组件。
  • 继承允许一个类(子类或派生类)继承另一类(父类或基类)的属性和方法。这促进了代码的重用并在类之间建立了层次关系。
  • 多态性是单个函数或方法根据其输入或调用它的对象以各种方式工作的能力。在C#中,多态性可以通过方法覆盖(使用override关键字)和方法隐藏(利用new关键字隐藏基类中的方法)来实现。
  • 抽象允许开发人员隐藏复杂的实现并仅显示对象的基本特征。这意味着用户仅与必要的内容进行交互,并且内部运作保持隐藏。在C#中,抽象类和接口是可以帮助实现抽象的工具。

这些原则有助于设计强大且可扩展的应用程序,从而易于维护和进一步开发。

封装和抽象

这些概念有助于管理对对象数据的访问并在编程中实现高级抽象:

  • 封装保护对象的内部状态并防止未经授权的外部访问,允许严格控制数据并确保数据完整性。
  • 抽象允许实现与接口分离,并支持创建具有更高灵活性和可扩展性的系统,使开发人员能够降低编程复杂性并提高效率。

C# 中,通过 privateprotectedpublic 等访问修饰符来确保封装。

这些修饰符决定类成员的可见性,允许隐藏实现细节并仅公开必要的 API。

  • public: 完全访问
  • private: 仅在类内部访问
  • protected: 类内部和派生类可访问
  • internal: 同一程序集内可访问
修饰符同一类中同一程序集内
派生类中
同一程序集内
非派生类中
不同程序集内
派生类中
不同程序集内
非派生类中
public✔️✔️✔️✔️✔️
private✔️
protected✔️✔️✔️
internal✔️✔️✔️
protected internal✔️✔️✔️✔️
private protected✔️✔️

继承和多态性

继承和多态性是面向对象编程的关键原则,可确保代码的可重用性和灵活性:

  • 继承允许创建一个新类,该类继承现有类的属性和方法,从而提高代码可重用性并在类之间建立层次关系。
  • 多态性是通过使用 virtualoverride 关键字重写子类中的方法的能力以及通过允许不同的类拥有一组一致的方法的接口来实现的。

2、类与对象 📝

类和对象是 C# 中面向对象编程的基本概念。类是一个用户自定义的数据类型,它封装了数据和操作数据的方法。对象是类的特定实例,代表已定义类的实现。

类的基础

提示

默认访问修饰符是 internal,成员的默认访问修饰符是 private

public class Character
{
// Properties
public string Name { get; set; }
public int Level { get; set; }
public int Health { get; set; }

// Constructor
public Character(string name)
{
Name = name;
Level = 1;
Health = 100;
}

// Method
public void LevelUp()
{
Level++;
Health += 10;
Console.WriteLine($"{Name} leveled up to level {Level}");
}

// Virtual method, can be overridden by subclasses
public virtual void Introduction()
{
Console.WriteLine($"I am {Name}, currently at level {Level}");
}
}

属性

C# 中,属性(Property)是一种特殊的成员,用于封装类中的字段并提供对其的访问。属性使得外部代码可以像访问字段一样访问类的内部数据,但通过属性的方式可以增加代码的安全性和可维护性。

属性通常由两个访问器(Accessor)组成:

  • get:用于获取属性的值。

  • set:用于设置属性的值。

public class Program
{
public static void Main()
{
// Create an instance of the Person class
Person person = new Person();
// Use the set accessor to set the Name property
person.Name = "Jack";
// Use the get accessor to retrieve the Name property and output it
Console.WriteLine("Person's name is: " + person.Name);
}
}

public class Person
{
private string name;

public string Name
{
get { return name; }
set { name = value; }
}
}

自动属性:编译器会自动生成一个私有字段来存储值。

public class Person
{
public string Name { get; set; }
}

只读属性 :只有get访问器,没有set访问器,只能在构造函数中设置。适用于需要计算或只提供读取功能的场景。

public class Circle
{
private double radius;

public Circle(double radius)
{
this.radius = radius;
}

public double Radius
{
get { return radius; }
}

public double Area
{
get { return Math.PI * radius * radius; }
}
}

只写属性 :只有set访问器,没有get访问器。适用于只需要设置值而不需要读取值的场景。

public class Account
{
private decimal balance;

public decimal Balance
{
set { balance = value; }
}
}

初始化器设置:在 C# 9.0中,引入的新特性,这一特性使得属性可以在对象初始化时被赋值,但一旦对象被创建后,属性就变为只读,无法再被修改。这种设计模式特别适合于不可变对象的创建。

public class Program
{
public static void Main()
{
var user = new User
{
Username = "Player",
Email = "player@example.com"
};
// user.Username = "Bob"; // This line will cause a compilation error because the Name property is read-only
Console.WriteLine($"User's Name is: {user.Username}");
Console.WriteLine($"User's Email is: {user.Email}");
}
}

public class User
{
public string Username { get; init; }
public string Email { get; init; }
}

构造函数

构造函数是创建对象时调用的特殊方法,它们初始化对象的状态。

默认构造函数
public class Person
{
public string Name;

public Person()
{
Name = "Player";
}
}
参数化构造函数
public class Book
{
public string Title;
public string Author;

public Book(string title, string author)
{
Title = title;
Author = author;
}
}

析构函数

析构函数用于在对象生命结束时执行清理操作,在游戏开发中,使用析构函数可以帮助管理资源和内存,以确保游戏运行的效率和稳定性。

析构函数是一个特殊的方法,具有以下特点:

  • 以波浪符(~)开头,后跟类名
  • 不能有访问修饰符
  • 不能有参数
  • 不能有返回类型
  • 每个类只能有一个析构函数
提示

析构函数的调用时间是不确定的,因为它依赖于垃圾回收器的运行时机。

public class Person
{
public Person()
{
Console.WriteLine("Constructor called");
}

~Person()
{
Console.WriteLine("Destructor called");
}
}

对象实例化

创建类的对象或实例。

// Base class
public class Player
{
// Properties
public string Name { get; set; }
public int Level { get; set; }

// Parameterless constructor
public Player()
{
Name = "Unnamed Player";
Level = 1;
}

// Constructor with parameters
public Player(string name)
{
Name = name;
Level = 1;
}

// Constructor with multiple parameters
public Player(string name, int level)
{
Name = name;
Level = level;
}
}

// Complex object example
public class GameCharacter
{
// Properties
public string Name { get; set; }
public int Health { get; set; }
public List<string> Skills { get; set; }

// Constructor
public GameCharacter()
{
Skills = new List<string>();
}
}

public class Program
{
public static void Main()
{
// 1. The most basic instantiation method
Player player1 = new Player();

// 2. Using constructors
Player player2 = new Player("Hero");
Player player3 = new Player("Mage", 10);

// 3. Object initializer
Player player4 = new Player
{
Name = "Archer",
Level = 5
};

// 4. var keyword
var player5 = new Player("Assassin", 8);

// 5. Explicit type instantiation
Player player6 = new("Knight", 12);

// 6. Collection initialization
var characters = new List<Player>
{
new Player("Warrior"),
new Player("Mage", 5),
new Player { Name = "Shooter", Level = 3 }
};

// 7. Complex object initialization
var advancedCharacter = new GameCharacter
{
Name = "Ultimate Hero",
Health = 100,
Skills = new List<string> { "Fireball", "Heal" }
};

// 8. Using factory method
Player specialPlayer = CreateSpecialPlayer();

// 9. Nullable type
Player? optionalPlayer = null;
}

// Factory method example
static Player CreateSpecialPlayer()
{
return new Player("Special Character", 20);
}
}

// Static constructor example
public class GameConfig
{
// Static field
public static int MaxLevel { get; private set; }

// Static constructor: will only be called once
static GameConfig()
{
MaxLevel = 100;
}
}

// Private constructor (Singleton pattern)
public class GameManager
{
// Private static instance
private static GameManager _instance;

// Private constructor
private GameManager() { }

// Public static method to get the instance
public static GameManager GetInstance()
{
if (_instance == null)
{
_instance = new GameManager();
}
return _instance;
}
}

3、继承与派生 📝

基类与派生类

基类(父类)定义基本属性和行为,派生类(子类)继承这些特性并可以添加自己的特性,一个类只能继承自一个基类。

C# 中使用冒号(:)来表示继承关系。

// Base class
public class Animal
{
public string Name { get; set; }
}

// Derived class
public class Dog : Animal
{
public string Breed { get; set; }
}

base 关键字

base 关键字允许您在派生类中调用基类的成员。它最常用于派生类中,以调用基类的构造函数或访问派生类中重写的其他基类成员。

public class Animal
{
public virtual void Eat()
{
Console.WriteLine("Animal is eating");
}
}

public class Dog : Animal
{
public override void Eat()
{
// Call base class method
base.Eat();
Console.WriteLine("Dog is eating bones");
}
}

this 关键字

this 关键字指向该类的当前实例。它通常用于指向当前对象的字段或方法,特别是当方法参数名称与类字段名称重叠时。

  1. 引用实例成员
public class Player
{
private string name;

public Player(string name)
{
// Use the this keyword to reference the member variable name
this.name = name;
}

public void DisplayInfo()
{
Console.WriteLine($"Player name: {this.name}");
}
}
  1. 调用其他构造函数
public class Character
{
private string name;
private int level;

// Constructor 1
public Character(string name) : this(name, 1) // Call Constructor 2
{

}

// Constructor 2
public Character(string name, int level)
{
this.name = name;
this.level = level;
}

public void DisplayInfo()
{
Console.WriteLine($"Name: {this.name}, Level: {this.level}");
}
}

方法重写

  • 基类方法必须使用 virtualabstractoverride 关键字标记
  • 派生类方法必须使用 override 关键字
  • 重写方法必须与基类方法具有相同的返回类型、名称、参数列表

在基类中,使用 virtual 关键字声明可以被重写的方法:

public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Some sound");
}
}

在派生类中,使用 override 关键字重写基类的虚方法:

public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof!");
}
}

抽象方法必须被派生类实现:

public abstract class Shape
{
public abstract double CalculateArea();

public virtual void Display()
{
Console.WriteLine("This is a shape");
}
}

public class Circle : Shape
{
private double radius;

public override double CalculateArea()
{
return Math.PI * radius * radius;
}
}

使用 sealed 关键字防止进一步重写:

public class Dog : Animal
{
public sealed override void MakeSound()
{
Console.WriteLine("Woof!");
}
}

4、接口与抽象类 📝

接口和抽象类是设计灵活且可维护的 C# 代码的重要工具。

接口

C#不支持类的多重继承。但是,一个类可以实现多个接口。

接口只包含方法声明,不包含实现。

提示

接口:定义行为

抽象类

抽象类可以包含带实现和不带实现的方法。

它们不能直接实例化。

一个类可以实现多个接口,但只能继承一个抽象类。

提示

抽象类:共享基础实现

// Interface definition
public interface IGameCharacter
{
string Name { get; set; }

void Attack();
void Defend();

void DisplayInfo()
{
Console.WriteLine($"Character Name: {Name}");
}
}

// Abstract class definition
public abstract class BaseCharacter
{
public string Name { get; set; }
public int Health { get; set; }

public BaseCharacter(string name)
{
Name = name;
Health = 100;
}

// Abstract method (must be implemented by subclasses)
public abstract void SpecialAbility();

// Concrete method
public virtual void TakeDamage(int amount)
{
Health -= amount;
Console.WriteLine($"{Name} took {amount} damage");
}
}

// Concrete class implementing the interface
public class Warrior : BaseCharacter, IGameCharacter
{
public string Weapon { get; set; }

public Warrior(string name, string weapon) : base(name)
{
Weapon = weapon;
}

// Interface method implementation
public void Attack()
{
Console.WriteLine($"{Name} attacks with {Weapon}!");
}

public void Defend()
{
Console.WriteLine($"{Name} raises a shield to defend!");
}

// Abstract method implementation
public override void SpecialAbility()
{
Console.WriteLine($"{Name} unleashes a berserker slash!");
}
}

public class Mage : BaseCharacter, IGameCharacter
{
public int ManaPoints { get; set; }

public Mage(string name) : base(name)
{
ManaPoints = 100;
}

// Interface method implementation
public void Attack()
{
Console.WriteLine($"{Name} casts a magical attack!");
ManaPoints -= 10;
}

public void Defend()
{
Console.WriteLine($"{Name} uses a magical barrier!");
}

// Abstract method implementation
public override void SpecialAbility()
{
Console.WriteLine($"{Name} unleashes a wide-area spell!");
}
}

// Multiple interface implementation
public interface IDamageable
{
void ReceiveDamage(int damage);
}

public interface IHealable
{
void Heal(int amount);
}

public class ComplexCharacter : IGameCharacter, IDamageable, IHealable
{
public string Name { get; set; }
public int Health { get; set; }

public void Attack() { }
public void Defend() { }

public void ReceiveDamage(int damage)
{
Health -= damage;
}

public void Heal(int amount)
{
Health += amount;
}
}

public class Program
{
public static void Main()
{
IGameCharacter warrior = new Warrior("Altaire", "Great Sword");
IGameCharacter mage = new Mage("Michael");

// Interface method calls
warrior.Attack();
mage.Attack();

// Abstract class method
BaseCharacter baseWarrior = new Warrior("Hero", "Long Sword");
baseWarrior.TakeDamage(20);
baseWarrior.SpecialAbility();

// Interface default implementation
warrior.DisplayInfo();
}
}

// Generic interface example
public interface IRepository<T>
{
void Add(T item);
T GetById(int id);
void Remove(T item);
}

public class CharacterRepository : IRepository<Warrior>
{
public void Add(Warrior item) { }
public Warrior GetById(int id) { return null; }
public void Remove(Warrior item) { }
}

5、类与类之间的关系 📝

从关系强度由弱到强排序为:关联 < 聚合 < 组合

组合

组合是一种强依赖的"整体-部分"关系,部分不能脱离整体而存在。

public class Weapon
{
public string Name { get; set; }
public int Damage { get; set; }

public void Attack()
{
Console.WriteLine($"{Name} attacks, dealing {Damage} damage");
}
}

public class Armor
{
public string Name { get; set; }
public int Defense { get; set; }

public void Protect()
{
Console.WriteLine($"{Name} provides {Defense} defense");
}
}

public class GameCharacter
{
// Composition: The character fully owns the weapon and armor
private Weapon _weapon;
private Armor _armor;

public GameCharacter(string weaponName, string armorName)
{
_weapon = new Weapon { Name = weaponName, Damage = 50 };
_armor = new Armor { Name = armorName, Defense = 30 };
}

public void Battle()
{
_weapon.Attack();
_armor.Protect();
Console.WriteLine("In battle...");
}
}

聚合

聚合表示"整体-部分"的关系,但部分可以独立存在。

public interface IWeapon
{
string Name { get; }
int Damage { get; }
}

public class Sword : IWeapon
{
public string Name { get; private set; }
public int Damage { get; private set; }

public Sword(string name, int damage)
{
Name = name;
Damage = damage;
}
}

public class Hero
{
public string Name { get; private set; }
public int Health { get; private set; }

// Aggregation: Can change weapons
private IWeapon _weapon;

public Hero(string name)
{
Name = name;
Health = 100;
}

// Equip weapon (aggregation relationship)
public void EquipWeapon(IWeapon weapon)
{
_weapon = weapon;
Console.WriteLine($"{Name} equipped {weapon.Name}");
}

public void Attack(Hero target)
{
if (_weapon != null)
{
int damage = _weapon.Damage;
target.TakeDamage(damage);
Console.WriteLine($"{Name} attacks {target.Name} with {_weapon.Name}");
}
else
{
Console.WriteLine($"{Name} has no weapon and cannot attack");
}
}

public void TakeDamage(int damage)
{
Health -= damage;
Console.WriteLine($"{Name} takes {damage} damage, remaining health {Health}");
}
}

public class Program
{
public static void Main()
{
Hero hero = new Hero("Hero");
Hero enemy = new Hero("Goblin");

IWeapon sword = new Sword("Hero's Sword", 20);
IWeapon axe = new Sword("Giant Axe", 25);

hero.EquipWeapon(sword);
enemy.EquipWeapon(axe);

hero.Attack(enemy);
enemy.Attack(hero);
}
}

关联

关联是对象之间最普通的引用关系,对象之间相互独立。

public class Player
{
public string Name { get; set; }

// Association: A player can have multiple characters
public List<Character> Characters { get; set; }

// Association: A player can join multiple guilds
public List<Guild> Guilds { get; set; }

public Player(string name)
{
Name = name;
Characters = new List<Character>();
Guilds = new List<Guild>();
}

public void AddCharacter(Character character)
{
Characters.Add(character);
}

public void JoinGuild(Guild guild)
{
Guilds.Add(guild);
guild.AddMember(this);
}
}

public class Character
{
public string Name { get; set; }
public int Level { get; set; }

// Association: A character belongs to a specific player
public Player Owner { get; set; }

// Association: A character can equip items
public List<Item> Equipment { get; set; }

public Character(string name, Player owner)
{
Name = name;
Owner = owner;
Equipment = new List<Item>();
}

public void EquipItem(Item item)
{
Equipment.Add(item);
}
}

public class Item
{
public string Name { get; set; }
public int Attack { get; set; }
public int Defense { get; set; }

// Association: An item can be owned by multiple characters
public List<Character> Owners { get; set; }

public Item(string name, int attack, int defense)
{
Name = name;
Attack = attack;
Defense = defense;
Owners = new List<Character>();
}
}

public class Guild
{
public string Name { get; set; }

// Association: A guild has multiple members
public List<Player> Members { get; set; }

public Guild(string name)
{
Name = name;
Members = new List<Player>();
}

public void AddMember(Player player)
{
Members.Add(player);
}
}

public class Program
{
public static void Main()
{
Player player = new Player("Hero");
Character warrior = new Character("Warrior", player);
Character mage = new Character("Mage", player);

player.AddCharacter(warrior);
player.AddCharacter(mage);

Item sword = new Item("Flame Dragon Sword", 50, 10);
Item shield = new Item("Iron Shield", 5, 30);

warrior.EquipItem(sword);
warrior.EquipItem(shield);

Guild heroGuild = new Guild("Hero League");

player.JoinGuild(heroGuild);

// Display associations
Console.WriteLine($"Player {player.Name} has {player.Characters.Count} characters");
Console.WriteLine($"Character {warrior.Name} has {warrior.Equipment.Count} pieces of equipment");
Console.WriteLine($"Guild {heroGuild.Name} has {heroGuild.Members.Count} members");
}
}

6、类的高级特性 📝

静态成员

以下都需要使用 static 关键字声明。

静态字段

不需要创建类的实例即可使用,通过类名直接访问ClassName.StaticFieldName

  • 存储在数据段中,在程序启动时就分配内存
  • 被类的所有实例共享,所有实例访问同一内存位置
  • 生命周期与应用程序相同,从程序启动到程序结束
静态方法

静态方法是属于类本身而不是类的实例的方法。

  • 不需要创建类的实例就能调用
  • 只能直接访问类的其他静态成员
  • 不能使用 this 关键字
静态构造函数

静态构造函数用于初始化类的静态成员或执行只应在类中发生一次的操作,而不是针对每个单独的对象。

  • 每个类只能有一个静态构造函数
  • 不能有参数
  • 不能有访问修饰符
  • 自动调用,不能手动调用
  • 在类第一次被使用前执行
静态类

静态类是一种特殊的类,只包含静态成员,不能被实例化。

  • 隐式密封(sealed),这意味着它们不能被继承
  • 不能包含实例构造函数
  • 不能被继承
静态属性

静态属性是属于类而不是实例的属性。

  • 通过类名直接访问的,不需要创建类的实例
  • 整个程序运行期间只存在一个共享的副本,所有对象共享这个属性
  • 可以是只读、只写或读写
public class GameSettings
{
// Static fields: Global game settings
public static int MaxLevel = 100;
public static int StartingGold = 1000;

// Static read-only field: Unmodifiable constant
public static readonly string GameVersion = "1.0.0";
}

public class Player
{
// Instance fields
public string Name { get; }
public int Gold { get; }

// Static field: Record total number of players
private static int _totalPlayers;

// Static property: Get current total number of players
public static int TotalPlayers => _totalPlayers;

public Player(string name)
{
Name = name;
Gold = GameSettings.StartingGold;

// Increment total players count for each new player created
_totalPlayers++;
}

// Static method: Reset player count
public static void ResetPlayerCount()
{
_totalPlayers = 0;
}
}

public class MonsterManager
{
// Static field: Global monster kill counter
private static int _totalMonstersKilled;

// Static method: Record monster kill
public static void RecordMonsterKill()
{
_totalMonstersKilled++;
}

// Static method: Get total kills
public static int GetTotalMonstersKilled()
{
return _totalMonstersKilled;
}
}

public class Program
{
public static void Main()
{
// Using static fields and methods
Console.WriteLine($"Game Version: {GameSettings.GameVersion}");
Console.WriteLine($"Max Level: {GameSettings.MaxLevel}");

// Create players
Player player = new Player("Hero");
if (player == null) throw new ArgumentNullException(nameof(player));

// Using static property
Console.WriteLine($"Current Total Players: {Player.TotalPlayers}");

// Simulate battles
MonsterManager.RecordMonsterKill();
MonsterManager.RecordMonsterKill();
MonsterManager.RecordMonsterKill();

Console.WriteLine($"Total Monsters Killed: {MonsterManager.GetTotalMonstersKilled()}");

// Demonstrate the characteristics of static fields
Console.WriteLine($"{player.Name}'s Initial Gold: {player.Gold}");
}
}

特性

特性用于将元数据添加到程序元素(例如类、方法和属性),这可以在运行时改变它们的行为。

预定义特性
  • Obsolete—— 标记已过时的代码

  • Serializable —— 标记可序列化的类

  • Conditional —— 用于条件编译

  • AttributeUsage —— 描述特性的使用方式

自定义特性
  • 必须继承自 Attribute

  • 类名通常以 Attribute 结尾

  • 可以包含构造函数和属性

using System.Reflection;

// Custom Attribute: Character Description
[AttributeUsage(AttributeTargets.Class)]
public class CharacterDescriptionAttribute(string description) : Attribute
{
public string Description { get; } = description;
}

// Attribute Validation
[AttributeUsage(AttributeTargets.Property)]
public class ValidRangeAttribute(int min, int max) : Attribute
{
public int Min { get; } = min;
public int Max { get; } = max;

public bool IsValid(int value)
{
return value >= Min && value <= Max;
}
}

// Character class using attributes
[CharacterDescription("Brave Adventurer")]
public class GameCharacter(string name, int level, int health)
{
public string Name { get; set; } = name;

[ValidRange(1, 100)]
public int Level { get; set; } = level;

[ValidRange(10, 500)]
public int Health { get; set; } = health;

// Method to validate properties using attributes
public bool Validate()
{
var properties = GetType().GetProperties();

foreach (var prop in properties)
{
if (prop.GetCustomAttributes(typeof(ValidRangeAttribute), false)
.FirstOrDefault() is ValidRangeAttribute validRangeAttr)
{
var value = (int)prop.GetValue(this);
if (!validRangeAttr.IsValid(value))
{
Console.WriteLine($"Property {prop.Name} validation failed: value must be between {validRangeAttr.Min} and {validRangeAttr.Max}");
return false;
}
}
}
return true;
}

// Get the description of the class
public string GetDescription()
{
var attribute = GetType().GetCustomAttribute<CharacterDescriptionAttribute>();
return attribute?.Description ?? "No description";
}
}

public class Program
{
public static void Main()
{
// Create character
GameCharacter hero = new GameCharacter("Hero", 50, 200);

// Validate character properties
if (hero.Validate())
{
Console.WriteLine("Character property validation passed");
}

// Get character description
Console.WriteLine($"Character description: {hero.GetDescription()}");

// Test invalid properties
GameCharacter invalidHero = new GameCharacter("Invalid Character", 150, 600);
if (!invalidHero.Validate())
{
Console.WriteLine("Character property validation failed");
}
}
}

反射

反射允许在运行时检查、修改和创建类型、方法和属性。

using System.Reflection;

// Base class for characters
public abstract class Character
{
public string Name { get; set; }
public int Health { get; set; }

public abstract void Attack();
}

// Warrior class
public class Warrior : Character
{
public int Strength { get; set; }

public Warrior(string name, int health, int strength)
{
Name = name;
Health = health;
Strength = strength;
}

public override void Attack()
{
Console.WriteLine($"{Name} attacks with a sword!");
}

public void SpecialSkill()
{
Console.WriteLine($"{Name} uses Fury Slash!");
}
}

// Mage class
public class Mage : Character
{
public int Mana { get; set; }

public Mage(string name, int health, int mana)
{
Name = name;
Health = health;
Mana = mana;
}

public override void Attack()
{
Console.WriteLine($"{Name} casts a magic attack!");
}

public void CastSpell()
{
Console.WriteLine($"{Name} casts a high-level spell!");
}
}

// Reflection helper class
public abstract class ReflectionHelper
{
// Create an object
public static object? CreateInstance(string typeName)
{
Type? type = Type.GetType(typeName);
return Activator.CreateInstance(type, "Default Character", 100, 50);
}

// Get all public methods
public static void GetMethods(object? obj)
{
Type? type = obj?.GetType();
Console.WriteLine($"All public methods of type {type?.Name}:");

MethodInfo[]? methods = type?.GetMethods(
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.DeclaredOnly
);

if (methods != null)
foreach (var method in methods)
{
Console.WriteLine(method.Name);
}
}

// Invoke a method
public static void InvokeMethod(object? obj, string methodName)
{
Type? type = obj?.GetType();
MethodInfo? method = type?.GetMethod(methodName);

if (method != null)
{
method.Invoke(obj, null);
}
else
{
Console.WriteLine($"Method {methodName} not found");
}
}

// Display property values
public static void DisplayProperties(object? obj)
{
Type? type = obj?.GetType();
PropertyInfo[]? properties = type?.GetProperties();

Console.WriteLine($"Properties of type {type?.Name}:");
if (properties != null)
foreach (var prop in properties)
{
object? value = prop.GetValue(obj);
Console.WriteLine($"{prop.Name}: {value}");
}
}
}

public class Program
{
public static void Main()
{
// Create an object using reflection
Warrior warrior = (Warrior)ReflectionHelper.CreateInstance("Warrior")!;

// Display object properties
ReflectionHelper.DisplayProperties(warrior);

// Get object methods
ReflectionHelper.GetMethods(warrior);

// Invoke specific methods
ReflectionHelper.InvokeMethod(warrior, "Attack");
ReflectionHelper.InvokeMethod(warrior, "SpecialSkill");

// Dynamically create and use an object
object? dynamicMage = ReflectionHelper.CreateInstance("Mage");
ReflectionHelper.InvokeMethod(dynamicMage, "Attack");
}
}

三、异常处理 🚨

异常处理是允许检测和处理程序执行期间发生的错误,防止崩溃和不可预见的结果。

1、基础异常处理 📝

C# 中,主要的错误处理机制基于 trycatchfinallythrow 结构的使用。

try-catch 块用于处理异常。可能引发异常的代码放在 try 块内,相应的异常处理代码放在 catch 块内。

try-catch 结构中 finally 块的作用是确保无论前面的 trycatch 块中是否抛出异常,其内部的代码都会被执行。这对于清理操作特别有用,例如关闭文件或数据库连接。

在大多数情况下,finally 块将被执行。但是,在极少数情况下,例如程序终止或灾难性异常(例如 StackOverflowException 或进程终止),finally 块可能无法执行,因为这些严重错误可能会中断程序执行的正常流程,并且应用程序将停止,从而导致 finally 块没有机会运行。

finally 可能无法执行的场景:

  1. 如果应用程序崩溃或被强制终止
  2. 如果 trycatch 块中存在无限循环
  3. 如果调用了 Environment.FailFast()Process.Kill()
  4. 在极端的系统级故障或硬件问题中
try
{
// Code that may throw an exception
int result = 10 / int.Parse("0");
}
catch (DivideByZeroException ex)
{
// Handling specific exception
Console.WriteLine($"Error: {ex.Message}");
}
catch (Exception ex)
{
// Handling any other exception
Console.WriteLine($"An unexpected error occurred: {ex.Message}");
}
finally
{
// Code that will be executed regardless of whether an exception occurred
Console.WriteLine("This code always executes, even if an exception occurred.");
}

您可以使用 throw 关键字手动引发异常。当您想要指示发生了某种情况或错误时,这非常有用。

public class Calculator
{
public int Divide(int dividend, int divisor)
{
if (divisor == 0)
{
throw new DivideByZeroException("Cannot divide by zero.");
}
return dividend / divisor;
}
}

2、常见异常类型 🔍

C# 具有多种异常类型,可以满足不同的异常场景。

  • ArgumentNullException:当传递给方法的参数为 null 时抛出此异常,当需要非空值时
  • ArgumentOutOfRangeException:当参数的值超出允许范围时会发生这种情况
  • DivideByZeroException:当尝试除以零时抛出此异常
  • InvalidOperationException:当对象的状态不允许特定操作时会出现这种情况
  • FileNotFoundException:当尝试访问的文件不存在时会发生这种情况
  • StackOverflowException:当由于过多递归或其他原因导致堆栈溢出时抛出此异常
  • NullReferenceException:当您尝试访问空引用对象的成员时会发生这种情况

3、自定义异常 📝

C# 中,异常被实现为从 Exception 基类继承的对象。这允许传递有关异常的附加信息并创建自定义异常类型。要创建您自己的异常类,只需从 Exception 类或其子类之一继承它即可。

您可以通过从 Exception 类派生来创建自己的自定义异常类。这允许您为您的应用程序定义特定的异常类型。

public class CustomException : Exception
{
public CustomException(string message) : base(message)
{

}
}
// Custom exception class
public class BusinessException : Exception
{
public string ErrorCode { get; }

public BusinessException(string message) : base(message)
{
}

public BusinessException(string message, string errorCode)
: base(message)
{
ErrorCode = errorCode;
}

public BusinessException(string message, Exception innerException)
: base(message, innerException)
{
}
}

// Using the custom exception
public class BusinessLogic
{
public void ProcessOrder(Order order)
{
if (order == null)
{
throw new BusinessException("Order cannot be null", "ORDER001");
}

if (order.Amount <= 0)
{
throw new BusinessException("Order amount must be greater than 0", "ORDER002");
}
}
}

4、异常过滤器 🔀

异常过滤器允许您根据特定条件捕获异常。

try
{
// Code that may throw an exception
int result = 10 / int.Parse("0");
}
catch (DivideByZeroException ex) when (ex.Message == "Attempted to divide by zero.")
{
// Handling specific exception with a filter
Console.WriteLine($"Error: {ex.Message}");
}

四、集合与泛型 📈

1、常用集合 🔗

C# 中的数组和集合用于存储数据,并允许以易于访问和操作的方式组织数据:

  • 数组是静态集合,能够存储固定数量的单一类型元素。
  • 集合是动态的,可以存储可变数量的元素;它们有不同的表现类型,例如列表、字典、堆栈、队列等。

.NET 提供了几种主要的集合类型:

  • List<T>:元素的动态数组。它维持顺序并允许重复元素。

  • Dictionary<TKey, TValue>:键值对的集合。它没有一个定义的顺序,并且键必须是唯一的。

  • HashSet<T>:一组唯一元素。它不维护任何特定的顺序。

  • Queue<T>:支持先进先出(FIFO) 操作的集合。

  • Stack<T>:支持后进先出 (LIFO) 操作的集合。

Array

C#中的数组是相同数据类型元素的固定大小集合。

// Declaration and Initialization
int[] numbers = new int[5] { 1, 2, 3, 4, 5 };

// Accessing Elements
int firstElement = numbers[0]; // Accessing the first element (index 0)

// Iterating Through Arrays
foreach (int number in numbers)
{
Console.WriteLine(number);
}

// Multidimensional Arrays
int[,] matrix = new int[3, 3]
{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};

List

列表提供动态大小调整和其他功能。

// Declaration and Initialization
List<string> names = new List<string>() { "Alice", "Bob", "Charlie" };

// Adding and Removing Elements
names.Add("David"); // Add an element
names.Remove("Bob"); // Remove an element by value
names.RemoveAt(0); // Remove an element by index

// Accessing Elements
string firstElement = names[0]; // Accessing the first element (index 0)

// Iterating Through Lists
foreach (string name in names)
{
Console.WriteLine(name);
}
信息

数组通常在索引访问和内存效率方面更快,当项目数量预先已知且保持不变时最为理想。List<T> 提供更多操作方法,更适合大小不确定的动态集合。

Dictionary

C# 中的字典是存储键值对的集合,可以根据关联的键快速访问值。

// Declaration and Initialization
Dictionary<string, int> ages = new Dictionary<string, int>()
{
{"Alice", 25},
{"Bob", 30},
{"Charlie", 22}
};

// Adding and Accessing Elements
ages["David"] = 28; // Add a new key-value pair
int bobAge = ages["Bob"]; // Access the value using the key

// Iterating Through Dictionaries
foreach (var pair in ages)
{
Console.WriteLine($"{pair.Key}: {pair.Value}");
}

// Checking for Key Existence
bool hasAlice = ages.ContainsKey("Alice"); // true
bool hasEve = ages.ContainsKey("Eve"); // false

HashSet

C# 中的 HashSet 是存储唯一元素的集合,没有任何特定的顺序。

// Declaration and Initialization
HashSet<int> uniqueNumbers = new HashSet<int>() { 1, 2, 3, 4, 5 };

// Adding and Removing Elements
uniqueNumbers.Add(6); // Add a new element
uniqueNumbers.Remove(3); // Remove an element

// Checking for Element Existence
bool hasThree = uniqueNumbers.Contains(3); // false
bool hasFive = uniqueNumbers.Contains(5); // true

// Set Operations
HashSet<int> otherNumbers = new HashSet<int>() { 4, 5, 6, 7, 8 };

// Union
HashSet<int> unionSet = new HashSet<int>(uniqueNumbers);
unionSet.UnionWith(otherNumbers); // {1, 2, 3, 4, 5, 6, 7, 8}

// Intersection
HashSet<int> intersectionSet = new HashSet<int>(uniqueNumbers);
intersectionSet.IntersectWith(otherNumbers); // {4, 5}

// Difference
HashSet<int> differenceSet = new HashSet<int>(uniqueNumbers);
differenceSet.ExceptWith(otherNumbers); // {1, 2, 3}
提示

当需要防止重复或执行频繁的查找操作时,它最为理想。

Queue 和 Stack

// Queue (First In First Out - FIFO)
Queue<string> queue = new Queue<string>();

// Enqueue
queue.Enqueue("First");
queue.Enqueue("Second");

// Dequeue
string item = queue.Dequeue();

// Peek at the front element without removing it
string peek = queue.Peek();

// Stack (Last In First Out - LIFO)
Stack<string> stack = new Stack<string>();

// Push onto the stack
stack.Push("First");
stack.Push("Second");

// Pop from the stack
string item = stack.Pop();

// Peek at the top element without removing it
string peek = stack.Peek();

2、泛型编程 🔀

高效、健壮的编程的核心在于编写经得起时间考验、适应不同场景并最大限度减少冗余的代码的能力。

泛型允许更抽象和通用的编码风格,而不是致力于特定的数据类型,确保您可以满足广泛的需求,而无需承担过多的代码重复的负担。

C# 中的泛型允许创建可操作不同数据类型的类、接口和方法,而不会损失类型安全性和性能。它们在创建可处理任何数据类型的多功能且灵活的集合、服务和其他组件方面发挥着关键作用。

与使用对象类型相比,泛型具有以下优点:

  • 类型安全:泛型确保您使用正确的数据类型,从而消除了运行时类型错误的风险。

  • 性能:使用泛型,在处理值类型时无需装箱或拆箱,从而提高运营效率。

  • 代码可重用性:泛型允许您编写一段可处理不同数据类型的代码,减少代码重复。

  • 消除类型转换:使用泛型,可以减少显式类型转换,从而使代码更干净、更具可读性。

在 .NET 中,泛型类型被编译为中间语言 (IL) 的单个模板。

当运行时需要特定类型实例时,即时 (JIT) 编译器会生成专用代码。

对于值类型(例如 int、double),将为每种类型生成单独的代码以确保优化性能。

然而,对于引用类型,相同的代码是共享的,从而使过程更加节省内存。

泛型可以与 C# 中的各种功能结合使用,例如:

  • 委托:您可以定义通用委托,它可以指向各种类型的方法。

  • 事件:事件可以基于通用委托。

  • 属性:虽然您无法创建通用属性类,但您可以将属性应用于通用结构。

泛型类

泛型类型扩展方法允许开发人员向现有类型(内置类型和用户定义类型)添加方法,而无需修改它们或创建新的派生类型。

// Basic Generic Class
public class GenericContainer<T>
{
private T _item;

public GenericContainer(T item)
{
_item = item;
}

public T GetItem()
{
return _item;
}

public void SetItem(T item)
{
_item = item;
}
}

// Multiple Type Parameters
public class KeyValuePair<TKey, TValue>
{
public TKey Key { get; set; }
public TValue Value { get; set; }

public KeyValuePair(TKey key, TValue value)
{
Key = key;
Value = value;
}
}

// Usage Example
public class GenericExample
{
public void UseGenericTypes()
{
var intContainer = new GenericContainer<int>(42);
var stringContainer = new GenericContainer<string>("Hello");
var pair = new KeyValuePair<int, string>(1, "One");
}
}

泛型方法

泛型方法是 C# 中一种特殊的方法类型,允许方法的参数类型在使用时才确定。

public class GenericMethods
{
// Basic Generic Method
public T GenericMethod<T>(T item)
{
Console.WriteLine($"Type: {typeof(T)}, Value: {item}");
return item;
}

// Generic Method with Multiple Type Parameters
public TResult Convert<TInput, TResult>(TInput input)
where TResult : new()
{
TResult result = new TResult();
// Conversion logic
return result;
}

// Generic Method in a Non-Generic Class
public static void Swap<T>(ref T first, ref T second)
{
T temp = first;
first = second;
second = temp;
}
}

泛型约束

约束可以应用于泛型,以限制可以用作参数的类型。

// Class Constraint
public class ClassConstraint<T> where T : class
{
public T Instance { get; set; }
}

// Value Type Constraint
public class ValueTypeConstraint<T> where T : struct
{
public T Value { get; set; }
}

// Constructor Constraint
public class NewConstraint<T> where T : new()
{
public T CreateNew()
{
return new T();
}
}

// Interface Constraint
public class InterfaceConstraint<T> where T : IComparable<T>
{
public bool IsGreaterThan(T first, T second)
{
return first.CompareTo(second) > 0;
}
}

// Multiple Constraints
public class MultipleConstraints<T>
where T : class, IDisposable, new()
{
public void ProcessItem(T item)
{
// Processing logic
item.Dispose();
}
}
// Base Class
public class Animal
{
public virtual void MakeSound() { }
}

// Generic Class with Base Class Constraint
public class AnimalContainer<T> where T : Animal
{
private T _animal;

public AnimalContainer(T animal)
{
_animal = animal;
}

public void MakeAnimalSound()
{
_animal.MakeSound();
}
}

// Usage Example
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof!");
}
}

public class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("Meow!");
}
}

泛型接口

// Generic Interface Definition
public interface IRepository<T>
{
T GetById(int id);
void Add(T item);
void Update(T item);
void Delete(int id);
IEnumerable<T> GetAll();
}

// Implementing the Generic Interface
public class Repository<T> : IRepository<T> where T : class
{
private readonly List<T> _items = new List<T>();

public T GetById(int id)
{
// Implement retrieval logic
return _items.FirstOrDefault();
}

public void Add(T item)
{
_items.Add(item);
}

public void Update(T item)
{
// Implement update logic
}

public void Delete(int id)
{
// Implement deletion logic
}

public IEnumerable<T> GetAll()
{
return _items;
}
}

C# 中的泛型支持协变和逆变,从而在类型之间实现更灵活的关系。

// Covariant Interface (out)
public interface IProducer<out T>
{
T Produce();
}

// Contravariant Interface (in)
public interface IConsumer<in T>
{
void Consume(T item);
}

// Implementation Example
public class Producer<T> : IProducer<T> where T : new()
{
public T Produce()
{
return new T();
}
}

public class Consumer<T> : IConsumer<T>
{
public void Consume(T item)
{
Console.WriteLine($"Consuming {item}");
}
}

// Using Covariance and Contravariance
public class VarianceExample
{
public void DemonstrateVariance()
{
IProducer<string> stringProducer = new Producer<string>();
IProducer<object> objectProducer = stringProducer; // Covariance

IConsumer<object> objectConsumer = new Consumer<object>();
IConsumer<string> stringConsumer = objectConsumer; // Contravariance
}
}

C# 中的泛型通过提供创建灵活且类型安全的组件的机制来增强代码的可重用性和可维护性。无论是使用泛型类、方法还是接口,理解泛型对于在 C# 中构建高效且适应性强的软件解决方案至关重要。

五、语言集成查询 (LINQ) 📚

LINQ 允许使用查询表达式与数据交互,而不管其来源如何。它有助于轻松地过滤、排序、分组和转换数据,提供一种无缝且集成的方式来查询对象、数据库和 XML 文档。

理解 LINQ 至关重要,因为它提供了统一且独立于模型的查询功能,简化了数据操作和检索过程,并且提供增强的可读性和可维护性。

C# 中,LINQ 是一组扩展,允许直接从编程语言对各种数据源执行查询。LINQ 可用于处理集合、XML、数据库等。

1、基础 📝

查询语法

var query = from item in collection
where item.Price > 100
orderby item.Name
select item;

方法语法

var query = collection
.Where(x => x.Price > 100)
.OrderBy(x => x.Name)
.Select(x => x);

2、常用 LINQ 操作符 📊

  • Where:此方法用于根据给定谓词过滤集合。它返回一个新集合,其中仅包含满足指定条件的元素。

  • Select:此方法用于投影或转换集合的元素。它返回一个新集合,其中包含已根据指定函数或投影进行转换的元素。

3、聚合操作 🔗

int count = numbers.Count();
int sum = numbers.Sum();
double average = numbers.Average();
int max = numbers.Max();
int min = numbers.Min();

4、分组和连接 📈

var groups = products.GroupBy(p => p.Category);

var joined = customers.Join(orders,
c => c.Id,
o => o.CustomerId,
(c, o) => new { Customer = c, Order = o });

5、延迟执行与立即执行 ⏰

LINQ 中的延迟执行意味着在枚举结果之前不会发生实际的数据处理或计算。当您构建 LINQ 查询时,它只是创建一个查询定义。

var query = numbers.Where(n => n > 3); // Query not executed yet
foreach (var num in query)
{
Console.WriteLine(num); // Query executed here
}

实际执行会延迟,直到您迭代查询结果,例如使用 foreach 循环或使用 ToList() 或 ToArray() 等方法转换结果。这可以通过避免不必要的计算来提高性能。

var result = numbers.Where(n => n > 3).ToList(); // Query executed immediately

调用以下方法会立即执行查询:

  • ToList()
  • ToArray()
  • Count()

然而,管理数据实际实现的时刻(即获取数据并将其加载到内存中)非常重要。过早具体化数据有时会消耗更多资源,尤其是当数据源很大(例如数据库)时。在决定具体化结果以优化资源使用和性能之前,您可能需要向查询附加更多条件或过滤器。

6、AllAny 🔁

用于检查集合中的元素。

  • All:检查集合中的每个元素是否满足特定条件。使用 All 时,您需要确保集合的所有元素都满足特定标准。

  • Any:检查集合中是否至少有一个元素满足特定条件。使用 Any 当您需要确定是否有任何元素满足特定标准时。

当集合为空时,会发生以下情况:

  • All:始终返回 true,因为不存在违反条件的元素。这可能看起来违反直觉,但在没有任何要检查的元素的情况下,它默认为 true。

  • Any:始终返回 false,因为不存在满足条件的元素。

例如,如果您想验证列表中的所有数字均为正数,则可以使用 All。如果您要检查列表中是否有负数,则可以使用 Any

int[] numbers = { 1, 3, 5, 8 };
bool hasEvenNumber = numbers.Any(n => n % 2 == 0);
Console.WriteLine($"Is there an even number? {hasEvenNumber}");

7、自定义 LINQ 扩展方法 📝

IEnumerableIQueryable 是表示 .NET 中集合的两个主要接口。这就是它们的作用:

IEnumerable:在内存中的对象级别操作。当您针对 IEnumerable 接口执行 LINQ 查询时,操作是在内存中执行的。它适合处理内存中的集合,例如数组或列表。

IQueryable:设计用于与外部数据源(例如数据库)交互。使用 IQueryable 进行的查询将转换为特定于数据源的查询(例如关系数据库的 SQL)。该接口允许延迟执行和内存不足 (OOM) 数据查询,使其对于大型数据集(尤其是数据库)非常高效。

这两个接口的主要区别在于执行位置:IEnumerable 处理内存中的数据。同时,IQueryable 允许构建表达式树,该表达式树可以转换为适合外部数据源的查询,例如数据库的 SQL。然后,它将解析后的查询发送到数据源进行处理,并以 IEnumerable 形式获取结果。

public class BasicConcepts
{
// IEnumerable: In-memory collection operations
// IQueryable: Database query operations

public void CompareInterfaces()
{
// IEnumerable - executed in memory
IEnumerable<int> enumerable = Enumerable.Range(1, 100);
var enumResult = enumerable
.Where(x => x > 50) // Filter in memory
.Select(x => x * 2); // Transform in memory

// IQueryable - executed at the data source
IQueryable<int> queryable = Enumerable.Range(1, 100).AsQueryable();
var queryResult = queryable
.Where(x => x > 50) // Translated to SQL WHERE clause
.Select(x => x * 2); // Translated to SQL SELECT clause
}
}

8、优化 💻

优化 LINQ 查询,尤其是针对大量数据集的查询,可以通过多种方法来实现:

  • 尽可能利用延迟执行,确保查询仅在结果是真正需要的情况下执行。这避免了不必要的计算。

  • 选择为您的特定用例量身定制的最有效的集合类型,因为底层数据结构会影响性能。

  • 在可行的情况下,使用 Take 等方法限制结果数据集的大小,以避免处理超出必要的数据。

  • 避免或明智地使用嵌套查询。由于多轮数据检索或计算,它们可能会导致性能问题。

  • 如果您预计对数据进行多次操作,请使用 ToArrayToList 等方法将结果具体化到内存中。这可以防止重复执行相同的 LINQ 查询。

六、Lambda 👀

Lambda 表达式和匿名函数是 C# 中的基本概念,它们允许就地声明和定义函数,通常用作其他函数的参数。

它们因能够提供简洁、富有表现力的语法来表示功能而闻名,尤其是与高阶函数和 LINQ 一起使用时。

1、Lambda 表达式 🔍

(parameters) => expression

public class LambdaBasics
{
public void BasicSyntax()
{
// 1. No-parameter Lambda
Action sayHello = () => Console.WriteLine("Hello");

// 2. Single-parameter Lambda
Func<int, int> square = x => x * x;

// 3. Multiple-parameter Lambda
Func<int, int, int> add = (x, y) => x + y;

// 4. Lambda with a statement block
Func<int, int, int> multiply = (x, y) =>
{
Console.WriteLine($"Multiplying {x} and {y}");
return x * y;
};

// 5. Type-declared Lambda
Func<double, double, double> divide = (double x, double y) => x / y;

// Usage examples
sayHello(); // Output: Hello
Console.WriteLine(square(5)); // Output: 25
Console.WriteLine(add(3, 4)); // Output: 7
}
}

2、Lambda 闭包 🔗

lambda 表达式和匿名方法上下文中的闭包是指这些构造从其封闭范围捕获并保留对其变量的访问的能力。

捕获的变量以即使在声明它们的方法完成执行后仍可访问和可变的方式存储。

如果没有正确理解,这可能会导致意外的行为,尤其是在多线程环境中,其中闭包可能会引入跨线程的共享状态。

public class LambdaClosure
{
public void ClosureExamples()
{
// Basic closure
int multiplier = 10;
Func<int, int> multiply = x => x * multiplier;

// Closure trap
var actions = new List<Action>();
for (int i = 0; i < 5; i++)
{
// Incorrect way - all actions will use the final value of i
actions.Add(() => Console.WriteLine(i));
}

// Correct way
for (int i = 0; i < 5; i++)
{
int temp = i;
actions.Add(() => Console.WriteLine(temp));
}

// Counter closure
Func<int> counter = CreateCounter();
Console.WriteLine(counter()); // 1
Console.WriteLine(counter()); // 2
}

private Func<int> CreateCounter()
{
int count = 0;
return () => ++count;
}
}

3、Lambda 表达式树 🌳

Lambda 表达式树是一种以树形数据结构来表示代码的方式,常用于动态构建查询。

public class ExpressionTrees
{
public void ExpressionTreeExamples()
{
// Create an expression tree
Expression<Func<int, bool>> isEven = x => x % 2 == 0;

// Manually construct an expression tree
ParameterExpression param = Expression.Parameter(typeof(int), "x");
BinaryExpression operation = Expression.Equal(
Expression.Modulo(param, Expression.Constant(2)),
Expression.Constant(0)
);
var manualIsEven = Expression.Lambda<Func<int, bool>>(
operation,
param
);

// Compile and execute
var compiled = isEven.Compile();
bool result = compiled(4); // true
}
}

4、将 Lambda 与 LINQ 结合使用 📈

Lambda 表达式在 LINQ 中的应用非常广泛,它使查询语法更简洁和清晰。

public class LinqWithLambda
{
private List<int> numbers = Enumerable.Range(1, 10).ToList();
private List<string> words = new List<string>
{
"apple", "banana", "cherry", "date"
};

public void LinqExamples()
{
// Where
var evenNumbers = numbers.Where(n => n % 2 == 0);

// Select
var doubled = numbers.Select(n => n * 2);

// OrderBy
var ordered = words.OrderBy(w => w.Length);

var filteredAndOrdered = numbers
.Where(n => n > 5)
.OrderByDescending(n => n)
.Select(n => n * n);

var grouped = words
.GroupBy(w => w[0])
.Select(g => new
{
FirstLetter = g.Key,
Words = g.ToList()
});
}
}