Skip to main content

常見踩坑總覽

以下踩坑模式從多份跨專案 Code Review 報告中提取,每個模式至少在兩個專案出現過。


Async / 同步阻塞

在非同步上下文中使用 .GetAwaiter().GetResult().Wait() 進行同步等待,導致 deadlock 風險和執行緒池耗盡。

錯誤做法
// 在 async 上下文中同步等待
FlushInternalAsync(CancellationToken.None).GetAwaiter().GetResult();
_cleanup.Wait(TimeSpan.FromSeconds(3));
正確做法
await FlushInternalAsync(cancellationToken);
// 若必須 fire-and-forget:
_ = Task.Run(() => FlushInternalAsync(ct), ct);

Dispose / 資源洩漏

IDisposable 資源(如 MatIModbusClientTimer)在某些程式碼路徑下未被正確 dispose,導致 memory leak 或 native resource 洩漏。

錯誤做法
var mat = new Mat(height, width, MatType.CV_8UC3, rawData);
_lastFrame = mat; // 舊的 _lastFrame 未 dispose
正確做法
var oldFrame = Interlocked.Exchange(ref _lastFrame, mat);
oldFrame?.Dispose();

Thread Safety / Race Condition

使用普通 bool flag 或非原子操作(如 _id++)進行並發控制,在多執行緒環境下產生 race condition。

錯誤做法
private bool _disposed;  // 非 volatile,可能讀到過期值
_transactionId++; // 非 atomic 操作
正確做法
private volatile bool _disposed;
Interlocked.Increment(ref _transactionId);

CancellationToken 缺失

公開的 async 方法未接受 CancellationToken 參數,或未將其正確傳遞給下游呼叫,導致操作無法被取消。

錯誤做法
Task<bool> LoginAsync(string username, string password);
正確做法
Task<bool> LoginAsync(string username, string password,
CancellationToken cancellationToken = default);

Fire-and-Forget 無錯誤處理

使用 _ = SomeMethodAsync() 啟動非同步工作但無錯誤處理策略,例外被靜默吞掉,導致初始化失敗或資料遺失無法偵測。

錯誤做法
_ = InitializeAsync(); // 例外靜默消失
正確做法
_ = InitializeAsync().ContinueWith(t =>
_logger.LogError(t.Exception, "Init failed"),
TaskContinuationOptions.OnlyOnFaulted);

DI Lifetime 設計錯誤

在 WPF 等無 per-request scope 的環境中,Singleton 服務注入 Scoped 依賴(如 DbContext),導致 DbContext 永不釋放、Change Tracker 累積、多執行緒並發存取非 thread-safe 物件。

錯誤做法
services.AddDbContext<PlcDbContext>(); // Scoped
services.AddSingleton<ITimeSeriesService, TimeSeriesService>();
// TimeSeriesService 注入 PlcDbContext → DbContext 被 Singleton 捕獲
正確做法
services.AddDbContextFactory<PlcDbContext>(opt =>
opt.UseNpgsql(connectionString));
// Service 內每次建立新的 DbContext:
using var ctx = _factory.CreateDbContext();

命名規範違規 — Boolean 屬性缺少前綴

Boolean 屬性未使用 Is/Has/Can/Should 前綴,違反 GST 命名規範,降低程式碼可讀性。

錯誤做法
public bool RequirePasswordExpiry { get; set; }
public bool AllowCrossUserSigning { get; set; }
正確做法
public bool IsPasswordExpiryRequired { get; set; }
public bool IsCrossUserSigningAllowed { get; set; }

空 Catch Block / 靜默吞掉例外

使用空的 catch 區塊或僅 catch 而不記錄任何資訊,導致錯誤被靜默吞掉,debug 極為困難。

錯誤做法
try { DecodeFrame(data); }
catch { } // 完全靜默
正確做法
try { DecodeFrame(data); }
catch (Exception ex)
{ _logger.LogDebug(ex, "Frame decode failed"); }

其他待補充項目

以下項目尚待充實,歡迎透過 Ingest 流程 補充:

  • 通訊協定:Modbus 設備離線偵測與重連策略、SECS/GEM 握手超時
  • 資料庫:TimescaleDB hypertable 分區策略、高頻寫入批次最佳化
  • 部署:.NET 單檔發布 vs 框架依賴部署、防毒軟體誤判
  • UI:WPF DataGrid 大量資料綁定效能、ComponentOne 授權驗證時機
  • RPA:圖像辨識解析度穩定性、SignalR 連線管理