瀏覽器歷史記錄的核心行為:線性瀏覽時尾端新增、**後退移動游標**、**前進移動游標**、**後退後新增新網址會截斷未來歷史**。`LinkedList
using System;
using System.Collections.Generic;
public class HistoryEntry
{
public string Url { get; set; }
public string Title { get; set; }
public string Html { get; set; } // 可選:快取內容離線檢視
public DateTime VisitedAt { get; } = DateTime.Now;
}
public class HistoryManager
{
private readonly LinkedList _list = new();
private LinkedListNode _current;
private readonly int _maxCount;
public HistoryManager(int maxCount = 100) => _maxCount = maxCount;
public HistoryEntry Current => _current?.Value;
public bool CanBack => _current?.Next != null;
public bool CanForward => _current?.Previous != null;
/// 新增歷史 (瀏覽新頁面)
public void Add(HistoryEntry entry)
{
// 連續重複網址不重複記錄
if (_current != null && _current.Value.Url.Equals(entry.Url, StringComparison.OrdinalIgnoreCase))
return;
// 後退後新增:截斷未來歷史 (Current 之後的節點)
if (_current != null && _current.Previous != null)
{
var toRemove = _current.Previous;
while (toRemove != null)
{
var next = toRemove.Previous;
_list.Remove(toRemove);
toRemove = next;
}
}
// 限制最大筆數:移除最舊 (尾端)
if (_maxCount > 0 && _list.Count >= _maxCount)
_list.RemoveLast();
// 插入頭端 (最新)
_current = new LinkedListNode(entry);
_list.AddFirst(_current);
}
public HistoryEntry Back()
{
if (!CanBack) return null;
_current = _current.Next;
return _current.Value;
}
public HistoryEntry Forward()
{
if (!CanForward) return null;
_current = _current.Previous;
return _current.Value;
}
public IEnumerable GetAll() => _list; // 最新在前
}
private readonly HistoryManager _history = new(50);
private void Navigate(string url)
{
// 1. 實際導航 (WebBrowser.Navigate / WebView2.CoreWebView2.Navigate)
// 2. 導航完成後記錄
_history.Add(new HistoryEntry { Url = url, Title = GetTitle() });
UpdateNavButtons();
}
private void BtnBack_Click(object sender, EventArgs e)
{
var entry = _history.Back();
if (entry != null) LoadEntry(entry);
UpdateNavButtons();
}
private void BtnForward_Click(object sender, EventArgs e)
{
var entry = _history.Forward();
if (entry != null) LoadEntry(entry);
UpdateNavButtons();
}
private void UpdateNavButtons()
{
btnBack.Enabled = _history.CanBack;
btnForward.Enabled = _history.CanForward;
}
| 場景 | 處理 |
|---|---|
| 重複網址 | `Add` 開頭檢查 `_current.Value.Url` 忽略大小寫比較 |
| 最大筆數 | 建構函式 `maxCount` 參數,超過移除 `RemoveLast()` (最舊) |
| 序列化儲存 | `_list.Select(n => n.Value).ToList()` 序列化 JSON/XML,還原時重建 `LinkedList` 並定位 `_current` |
| 多標籤頁 | 每個 Tab 獨立一個 `HistoryManager` 實例 |
| 效能 | 所有操作 O(1),記憶體開銷僅節點物件 |
| 結構 | 優點 | 缺點 | 適合度 |
|---|---|---|---|
LinkedList | 雙向、O(1) 插刪、游標天然對應 | 無索引存取、記憶體碎片 | ★★★★★ 最佳 |
List | 索引快、記憶體緊湊 | 截斷未來需 `RemoveRange` O(n)、插入頭端 O(n) | ★★★☆☆ 可接受 |
兩個 Stack (back/forward) | 概念直觀 | 無法直接實現「後退後新增截斷未來」需額外邏輯 | ★★☆☆☆ 較麻煩 |
`LinkedList