哈希表(HashtableDictionary<TKey, TValue>

  • 原理:哈希表通过哈希函数将键映射到一个哈希码,进而确定值的存储位置,实现快速查找。Hashtable 是早期的哈希表实现,Dictionary<TKey, TValue> 是泛型版本。
  • 特点
    • 快速查找:平均情况下,查找、插入和删除操作的时间复杂度接近 \(O(1)\)。
    • 键唯一性:键必须唯一,否则会覆盖已有的键值对。
  • 示例(Dictionary<TKey, TValue>
Dictionary<string, int> dict = new Dictionary<string, int>();
dict.Add("one", 1);
int value;
bool success = dict.TryGetValue("one", out value);

有序列表(SortedList<TKey, TValue>SortedDictionary<TKey, TValue>

  • 原理:有序列表会根据键的顺序对元素进行排序存储。SortedList<TKey, TValue> 内部使用数组和二分查找,SortedDictionary<TKey, TValue> 使用红黑树。
  • 特点
    • 按键排序:元素按键的顺序排列。
    • 查找效率SortedDictionary<TKey, TValue> 的插入和删除操作平均时间复杂度为 \(O(\log n)\),SortedList<TKey, TValue> 在插入和删除大量元素时性能稍逊,因为可能需要重新分配数组空间,但查找性能也为 \(O(\log n)\)。
  • 示例(SortedDictionary<TKey, TValue>
SortedDictionary<int, string> sortedDict = new SortedDictionary<int, string>();
sortedDict.Add(3, "three");
sortedDict.Add(1, "one");
foreach (var pair in sortedDict)
{
    Console.WriteLine($"{pair.Key}: {pair.Value}");
}
  1. 栈和队列

栈(Stack<T>

原理:遵循后进先出(LIFO, Last In First Out)原则。元素从栈顶压入和弹出。

Stack<int> stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
int top = stack.Pop(); // 返回2

队列(Queue<T>

原理:遵循先进先出(FIFO, First In First Out)原则。元素从队尾入队,从队头出队。

Queue<int> queue = new Queue<int>();
queue.Enqueue(1);
queue.Enqueue(2);
int first = queue.Dequeue(); // 返回1

类和对象的创建

类的定义:类是一种用户自定义的数据类型,封装了数据(字段和属性)和行为(方法)。

  • 对象的创建:使用 new 关键字创建类的实例(对象)。
class Person
{
    // 字段
    private string name;
    // 属性
    public string Name
    {
        get { return name; }
        set { name = value; }
    }
    // 构造函数
    public Person(string initialName)
    {
        name = initialName;
    }
}
class Program
{
    static void Main()
    {
        Person person = new Person("Alice");
        Console.WriteLine(person.Name);
    }
}

属性和字段

  • 字段:是类中用于存储数据的变量,直接表示数据状态。通常声明为 private 以实现封装。
  • 属性:是一种特殊的成员,提供对字段的访问控制。属性有 getset 访问器,可以包含逻辑。
class Circle
{
    // 字段
    private double radius;
    // 属性
    public double Radius
    {
        get { return radius; }
        set
        {
            if (value > 0)
            {
                radius = value;
            }
        }
    }
}

字段和属性的区别

  • 封装性:字段通常设为 private,直接访问可能破坏数据完整性;属性通过 getset 访问器控制访问,实现更好的封装。
  • 数据验证:属性的 set 访问器可进行数据验证,字段无法直接进行。
  • 可观察性:属性可添加逻辑,如日志记录,字段无此功能。

属性拦截(System.ComponentModel.INotifyPropertyChanged

  • 用途:用于通知客户端(如 UI)属性值已更改。
using System.ComponentModel;
class ObservableObject : INotifyPropertyChanged
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name!= value)
            {
                _name = value;
                OnPropertyChanged(nameof(Name));
            }
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

构造函数

  • 用途:用于初始化对象的状态,在创建对象时自动调用。
class Rectangle
{
    private int width;
    private int height;
    public Rectangle(int w, int h)
    {
        width = w;
        height = h;
    }
}

析构函数

  • 用途:用于在对象被销毁时执行清理操作,如释放非托管资源。
class ResourceHolder
{
    // 假设这里有非托管资源
    ~ResourceHolder()
    {
        // 清理非托管资源的代码
    }
}
  • 注意:在 C# 中,垃圾回收器会自动管理对象的生命周期,析构函数通常用于处理非托管资源。但尽量使用 IDisposable 接口和 using 语句来进行资源清理,因为析构函数的调用时间不确定。

更多推荐