跳到主要内容

C# 高级特性

一、内存管理和垃圾回收 📦

C#中,由于垃圾收集器机制,内存管理是自动处理的。

信息

垃圾回收 (GC) 是运行时自动回收不再使用的对象所占用的内存的过程,可以防止内存泄漏并促进内存的高效利用。

它自动识别程序不再使用的对象并释放它们占用的内存。虽然垃圾收集器简化了内存管理,但开发人员需要仔细管理不受 .NET 垃圾收集器控制的非托管资源,例如数据库连接或文件流。

提示

栈和堆:在 C# 中,内存分为两个主要区域:栈和堆。栈用于存储值类型和方法调用信息,而堆用于动态分配内存,主要用于引用类型。

如果开发人员不释放这些资源,它们将在应用程序的整个生命周期中持续存在,可能导致内存泄漏和系统压力。

提示

值类型与引用类型:值类型(例如,int,float)存储在栈上,而引用类型(例如,类,数组)分配在堆上。引用类型存储对堆上实际数据的引用。

IDisposable 接口

IDisposable 接口用于释放非托管资源,例如文件句柄、数据库连接或网络套接字。

public class ResourceManager : IDisposable
{
private bool disposed = false; // Track whether resources have been released
private IntPtr handle; // Example: unmanaged resource
private Component component; // Example: managed resource

// Public Dispose method - called through the interface
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); // Prevent finalizer from running
}

// Protected virtual Dispose method - allows derived classes to override
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Release managed resources
if (component != null)
{
component.Dispose();
component = null;
}
}

// Release unmanaged resources
if (handle != IntPtr.Zero)
{
Marshal.FreeHGlobal(handle);
handle = IntPtr.Zero;
}

disposed = true;
}
}

~ResourceManager()
{
Dispose(false);
}

// Check if disposed before use
protected void ThrowIfDisposed()
{
if (disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
}
}
using 语句
// Traditional syntax
using (var resource = new ResourceManager())
{
resource.DoWork();
} // Automatically calls Dispose()

// C# 8.0+ simplified syntax
using var resource = new ResourceManager();
resource.DoWork();
// Dispose() is automatically called at the end of the scope

二、文件 IO 操作与序列化 💻

文件输入/输出(I/O)和序列化是 C# 编程的重要方面,涉及从文件中读取和写入,以及将对象转换为适合存储或传输的格式。

1、文件 I/O 📁

// Reading text from a file
string filePath = "example.txt";
string content = File.ReadAllText(filePath);

// Writing text to a file
string filePath = "example.txt";
string content = "Hello, File I/O!";
File.WriteAllText(filePath, content);

// Reading lines from a file
string filePath = "example.txt";
string[] lines = File.ReadAllLines(filePath);

// Writing lines to a file
string filePath = "example.txt";
string[] lines = { "Line 1", "Line 2", "Line 3" };
File.WriteAllLines(filePath, lines);
using System.IO;

// Define the file path
string filePath = "example.txt";

// Checking if a file exists
bool fileExists = File.Exists(filePath);
if (fileExists)
{
// Deleting a file
File.Delete(filePath);
Console.WriteLine("File deleted successfully.");
}
else
{
Console.WriteLine("File does not exist.");
}

2、序列化 🔗

序列化是将对象或数据结构转换为易于存储或传输的格式的过程。

二进制序列化

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

[Serializable]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}

Person person = new Person { Name = "John", Age = 30 };

BinaryFormatter binaryFormatter = new BinaryFormatter();

using (FileStream stream = new FileStream("person.bin", FileMode.Create))
{
binaryFormatter.Serialize(stream, person);
}

二进制反序列化

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

[Serializable]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}

// Binary deserialization
BinaryFormatter binaryFormatter = new BinaryFormatter();

using (FileStream stream = new FileStream("person.bin", FileMode.Open))
{
Person deserializedPerson = (Person)binaryFormatter.Deserialize(stream);
Console.WriteLine($"Name: {deserializedPerson.Name}, Age: {deserializedPerson.Age}");
}

JSON 序列化和反序列化

using System.Text.Json;
using System.Text.Json.Serialization;

public class Character
{
// JSON property renaming
[JsonPropertyName("character_name")]
public string Name { get; set; }

public int Level { get; set; }

// Ignore serialization
[JsonIgnore]
public string SecretCode { get; set; }

// Conditional serialization
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Title { get; set; }

// Collection property
public List<Item> Inventory { get; set; }
}

public class Item
{
public string Name { get; set; }
public int Quantity { get; set; }

// Custom serialization converter
[JsonConverter(typeof(CustomPriceConverter))]
public decimal Price { get; set; }
}

public class CustomPriceConverter : JsonConverter<decimal>
{
public override decimal Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
// Custom deserialization logic
return reader.GetDecimal() * 1.1m;
}

public override void Write(
Utf8JsonWriter writer,
decimal value,
JsonSerializerOptions options)
{
// Custom serialization logic
writer.WriteNumberValue(Math.Round(value, 2));
}
}

// JSON serialization manager
public class JsonSerializationManager
{
// Basic serialization
public static string Serialize(object obj)
{
// Configure serialization options
var options = new JsonSerializerOptions
{
WriteIndented = true, // Pretty print
PropertyNameCaseInsensitive = true
};

return JsonSerializer.Serialize(obj, options);
}

// Basic deserialization
public static T? Deserialize<T>(string json)
{
return JsonSerializer.Deserialize<T>(json);
}

// File serialization
public static void SerializeToFile(object obj, string filePath)
{
string json = Serialize(obj);
File.WriteAllText(filePath, json);
}

// Deserialize from file
public static T? DeserializeFromFile<T>(string filePath)
{
string json = File.ReadAllText(filePath);
return Deserialize<T>(json);
}
}

public class Program
{
public static void Main()
{
// Create character
var character = new Character
{
Name = "Hero Allen",
Level = 10,
SecretCode = "HERO123",
Title = "Savior",
Inventory =
[
new() { Name = "Magic Sword", Quantity = 1, Price = 100.50m },
new() { Name = "Healing Potion", Quantity = 5, Price = 20.75m }
]
};

// Basic serialization
string jsonString = JsonSerializationManager.Serialize(character);
Console.WriteLine("Serialization result:");
Console.WriteLine(jsonString);

// Deserialization
var deserializedCharacter = JsonSerializationManager.Deserialize<Character>(jsonString);
Console.WriteLine("\nDeserialization result:");
Console.WriteLine($"Name: {deserializedCharacter?.Name}");
if (deserializedCharacter != null) Console.WriteLine($"Level: {deserializedCharacter.Level}");

// File serialization
string filePath = "character.json";
JsonSerializationManager.SerializeToFile(character, filePath);
Console.WriteLine($"\nSaved to file: {filePath}");

// Deserialize from file
var fileCharacter = JsonSerializationManager.DeserializeFromFile<Character>(filePath);
Console.WriteLine("\nFile deserialization result:");
Console.WriteLine($"Name: {fileCharacter?.Name}");
if (fileCharacter != null) Console.WriteLine($"Item count: {fileCharacter.Inventory.Count}");
}
}

三、委托与事件 👥

委托和事件是 C# 中的基本概念,有助于实现事件驱动编程。它们提供了一种以解耦和可扩展的方式处理和响应事件的方法。

虽然委托本质上是类型安全的函数指针,可以指向一个或多个方法,但事件是一种机制,允许类在发生感兴趣的事情时通知其他类或对象。

1、委托 📝

C# 中的委托是可以指向方法的对象。它们允许以类型安全的方式实现函数指针概念。委托通常用于实现事件和回调。

委托是安全封装方法的类型,并允许定义可保存对这些方法的引用的变量,从而启用回调机制和事件处理。

委托声明

public delegate void MyDelegate(string message);

使用委托

public class MessageProcessor
{
public void ProcessMessage(string message)
{
Console.WriteLine($"Processing message: {message}");
}
}

// Delegate declaration
public delegate void MyDelegate(string message);

public class Program
{
public static void Main()
{
MessageProcessor processor = new MessageProcessor();
MyDelegate delegateInstance = processor.ProcessMessage;

// Invoking the delegate
delegateInstance("Hello, Delegate!");
}
}

多播委托

允许一个委托实例持有多个方法的引用,当调用该委托时,所有被引用的方法都会按照添加顺序依次执行。

这可能会增加管理和调试的复杂性,如果处理不当,可能会导致意想不到的副作用。

public class Calculator
{
public void Add(int a, int b)
{
Console.WriteLine($"Sum: {a + b}");
}

public void Multiply(int a, int b)
{
Console.WriteLine($"Product: {a * b}");
}
}

// Delegate declaration
public delegate void MyDelegate(int a, int b);

public class Program
{
public static void Main()
{
Calculator calculator = new Calculator();

MyDelegate addDelegate = calculator.Add;
MyDelegate multiplyDelegate = calculator.Multiply;

// Creating a multicast delegate
MyDelegate multicastDelegate = addDelegate + multiplyDelegate;

// Invoking both methods
multicastDelegate(3, 4);
}
}

内置泛型委托类型

C# 中,Func<T> 用于返回值的委托,Action<T> 用于不返回值的委托,Predicate<T> 用于返回布尔值的委托。

Func<int, int, int> add = (a, b) => a + b;
int result = add(5, 3); // result = 8

// Calculate damage
Func<int, float, int> calculateDamage = (baseDamage, criticalMultiplier) =>
(int)(baseDamage * criticalMultiplier);
Action<string> logMessage = message => Console.WriteLine(message);
logMessage("Player attacked!");

// Character action
Action<Character> playerAttack = character => {
character.TakeDamage(10);
character.PlayAttackAnimation();
};
Predicate<int> isEven = number => number % 2 == 0;
bool result = isEven(4); // true

// Enemy filtering
Predicate<Enemy> isLowHealth = enemy => enemy.Health < 20;
List<Enemy> weakEnemies = enemyList.FindAll(isLowHealth);

2、事件 📣

事件使用委托来通知状态更改,允许一个对象通知其他对象有关某些事件的信息。

事件声明

public class EventPublisher
{
public event MyDelegate MyEvent;
}

订阅事件

public class EventSubscriber
{
public void Subscribe(EventPublisher publisher)
{
publisher.MyEvent += HandleEvent;
}

public void HandleEvent(string message)
{
Console.WriteLine($"Event handled: {message}");
}
}

发布事件

EventPublisher publisher = new EventPublisher();
EventSubscriber subscriber = new EventSubscriber();
subscriber.Subscribe(publisher);
publisher.MyEvent?.Invoke("Event message"); // Raising the event

事件处理程序和 EventArgs

public class CustomEventArgs : EventArgs
{
public string EventMessage { get; }

public CustomEventArgs(string message)
{
EventMessage = message;
}
}

public class EventPublisher
{
public event EventHandler<CustomEventArgs> MyEvent;

public void RaiseEvent(string message)
{
OnMyEvent(new CustomEventArgs(message));
}

protected virtual void OnMyEvent(CustomEventArgs e)
{
MyEvent?.Invoke(this, e);
}
}

3、综合例子 📚

// Define delegate types
public delegate void AttackDelegate(string target);
public delegate int CalculateDamageDelegate(int baseAttack, int criticalChance);

public class GameCharacter(string name, int health, int attack)
{
// Event delegates
public event AttackDelegate? OnAttack;
public event Action<string>? OnDeath;

public string Name { get; } = name;
private int Health { get; set; } = health;
private int Attack { get; } = attack;

// Perform attack using delegate
public void PerformAttack(string target, CalculateDamageDelegate damageCalculator)
{
// Calculate damage
int damage = damageCalculator(Attack, 20);

// Trigger attack event
OnAttack?.Invoke(target);

Console.WriteLine($"{Name} attacks {target}, dealing {damage} damage");
}

public void TakeDamage(int damage)
{
Health -= damage;

if (Health <= 0)
{
// Trigger death event
OnDeath?.Invoke($"{Name} has fallen");
}
}
}

public abstract class SkillManager
{
// Multicast delegate
public delegate void SkillEventHandler(string skillName);
public static event SkillEventHandler? OnSkillUsed;

public static void UseSkill(string skillName, Action? skillAction)
{
// Trigger skill event
OnSkillUsed?.Invoke(skillName);

// Execute skill action
skillAction?.Invoke();
}
}

public class CombatLogger
{
public void LogAttack(string target)
{
Console.WriteLine($"[Log] Attack target: {target}");
}

public void LogDeath(string deathMessage)
{
Console.WriteLine($"[Log] {deathMessage}");
}
}

public class Program
{
public static void Main()
{
GameCharacter hero = new GameCharacter("Hero", 100, 20);
GameCharacter monster = new GameCharacter("Goblin", 50, 10);
CombatLogger logger = new CombatLogger();

// Register events
hero.OnAttack += logger.LogAttack;
hero.OnDeath += logger.LogDeath;
monster.OnDeath += logger.LogDeath;

// Register skill event
SkillManager.OnSkillUsed += skillName =>
Console.WriteLine($"[Skill] Used skill: {skillName}");

// Define damage calculation delegate
CalculateDamageDelegate standardDamage = (baseAttack, criticalChance) =>
{
Random random = new Random();
bool isCritical = random.Next(100) < criticalChance;
return isCritical ? baseAttack * 2 : baseAttack;
};

SkillManager.UseSkill("Fireball", () =>
{
Console.WriteLine("Casting a powerful fireball!");
});

hero.PerformAttack(monster.Name, standardDamage);
monster.TakeDamage(25);

// Func and Action delegate examples
Func<int, int, int> multiply = (a, b) => a * b;
Action<string> print = Console.WriteLine;

Console.WriteLine($"Multiplication result: {multiply(5, 3)}");
print("Delegate usage example");
}
}

五、基本原则 🔝

  • 可读性:代码应该易于阅读和理解,不仅对于创建者而且对于其他开发人员。这有利于更顺畅的协作和知识转移。
  • 模块化:代码应分为可独立测试和使用的逻辑块或模块。这种方法有助于隔离问题并增强代码的可重用性。
  • 不要重复 (DRY) :通过利用提高代码可重用性的方法、函数和类来避免代码重复,这有助于更轻松地维护代码并减少出现错误的可能性。
  • 清晰的接口:函数和类应该具有清晰且易于理解的接口,以促进不同代码组件之间更好的交互和集成。
  • 简单性:避免复杂而复杂的结构,这会使代码维护更具挑战性。力求简单和清晰,使代码更易于理解和修改。

六、设计模式 👥

设计模式是针对软件设计中遇到的常见问题的可重用解决方案。它们提供了一种结构化的方法来解决某些类型的问题,并促进代码更加模块化、可维护和可扩展。

C# 中,我经常使用单例、工厂、观察者、策略和装饰者等模式。

1、单例模式 📝

单例模式确保一个类在整个系统中只有一个实例,并提供对该实例的全局访问点。这对于管理应限制为单个实例的资源非常有用,确保一致性并防止资源使用中的潜在冲突。

C# 中的单例模式可以使用类的静态实例与私有构造函数相结合来实现。要注意的主要问题之一是多线程环境,这可能会导致创建多个实例。这可以通过采用锁定机制或延迟初始化来规避。

public class Singleton
{
private static Singleton instance;
private Singleton() { }

public static Singleton Instance
{
get
{
if (instance == null)
{
instance = new Singleton();
}

return instance;
}
}
}

2、工厂模式 🏭

定义用于创建对象的接口,但允许子类更改将要创建的对象的类型。

工厂模式有助于将对象创建逻辑与主客户端代码隔离开来,从而提高系统灵活性和可扩展性。这也有助于在不修改现有代码的情况下添加新的对象类型,从而增强系统的可维护性和可扩展性。

public interface IProduct
{
void Produce();
}

public class ConcreteProductA : IProduct
{
public void Produce()
{
Console.WriteLine("Producing Product A");
}
}

public class ConcreteProductB : IProduct
{
public void Produce()
{
Console.WriteLine("Producing Product B");
}
}

public interface IFactory
{
IProduct CreateProduct();
}

public class ConcreteFactoryA : IFactory
{
public IProduct CreateProduct()
{
return new ConcreteProductA();
}
}

public class ConcreteFactoryB : IFactory
{
public IProduct CreateProduct()
{
return new ConcreteProductB();
}
}

3、观察者模式 👀

定义对象之间的一对多依赖关系,以便当一个对象状态改变时,所有依赖于它的对象都会被通知并自动更新。

public interface IObserver
{
void Update(string message);
}

public class ConcreteObserver : IObserver
{
private readonly string name;

public ConcreteObserver(string name)
{
this.name = name;
}

public void Update(string message)
{
Console.WriteLine($"{name} received message: {message}");
}
}

public interface ISubject
{
void Attach(IObserver observer);
void Detach(IObserver observer);
void Notify(string message);
}

public class ConcreteSubject : ISubject
{
private readonly List<IObserver> observers = new List<IObserver>();

public void Attach(IObserver observer)
{
observers.Add(observer);
}

public void Detach(IObserver observer)
{
observers.Remove(observer);
}

public void Notify(string message)
{
foreach (var observer in observers)
{
observer.Update(message);
}
}
}

4、策略模式 🔩

定义一系列算法,封装每个算法,并使它们可以互换。

public interface IStrategy
{
void Execute();
}

public class ConcreteStrategyA : IStrategy
{
public void Execute()
{
Console.WriteLine("Executing Strategy A");
}
}

public class ConcreteStrategyB : IStrategy
{
public void Execute()
{
Console.WriteLine("Executing Strategy B");
}
}

public class Context
{
private IStrategy _strategy;

public Context(IStrategy strategy)
{
_strategy = strategy;
}

public void SetStrategy(IStrategy strategy)
{
_strategy = strategy;
}

public void ExecuteStrategy()
{
_strategy.Execute();
}
}

5、装饰器模式 🎨

动态地将额外的责任附加到对象上。装饰器提供了一种灵活的替代方案,用于通过子类化扩展功能。

public interface IComponent
{
void Operation();
}

public class ConcreteComponent : IComponent
{
public void Operation()
{
Console.WriteLine("Concrete Component Operation");
}
}

public abstract class Decorator : IComponent
{
protected IComponent component;

public Decorator(IComponent component)
{
this.component = component;
}

public virtual void Operation()
{
if (component != null)
{
component.Operation();
}
}
}

public class ConcreteDecoratorA : Decorator
{
public ConcreteDecoratorA(IComponent component) : base(component) { }

public override void Operation()
{
base.Operation();
Console.WriteLine("Concrete Decorator A Operation");
}
}

public class ConcreteDecoratorB : Decorator
{
public ConcreteDecoratorB(IComponent component) : base(component) { }

public override void Operation()
{
base.Operation();
Console.WriteLine("Concrete Decorator B Operation");
}
}