編程小知識之循環依賴

  • 2019 年 11 月 3 日
  • 筆記

版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。

本文鏈接:https://blog.csdn.net/tkokof1/article/details/102815430

之前工作中遇到了循環依賴的問題,在此簡單記錄一些相關的知識。

拿 Lua(5.3) 舉例,如果我們循環 require 模組,就會觸發堆棧溢出錯誤:

-- module_1.lua  require("module_2")  return {}
-- module_2.lua  require("module_1")  return {}
-- test.lua  -- cause stack overflow here ...  require("module_1")

使用 Lua require 的模組只有兩種狀態: 未載入已載入,並沒有所謂的 部分載入 的概念,這也導致了 Lua require 不能處理循環依賴問題,類似的,我們也可以看看 C# 中涉及循環依賴的表現:

我們都知道 C# 中類的靜態構造函數在創建第一個類型實例或者引用類型任一靜態成員之前會被調用,據此,我們可以編寫兩個相互引用的靜態構造函數來進行循環依賴的測試:

class ClassA  {      public static int s_value;        static ClassA()      {          s_value = ClassB.s_value;      }  }    class ClassB  {      public static int s_value;        static ClassB()      {          s_value = ClassA.s_value;      }  }    static void Main(string[] args)  {      Console.WriteLine(ClassA.s_value);      Console.WriteLine(ClassB.s_value);  }

也許你會猜測上述程式碼也會產生堆棧溢出之類的問題,但實際上,程式會正常輸出 0 0,原因在於 C# 並不會重複執行類的靜態構造函數,哪怕類的靜態構造函數還沒有執行完成(正在執行),簡單來說, C# 中類的靜態構造函數可以處理循環依賴的問題,只是執行結果可能並不直觀:

(有興趣的朋友可以考慮看看下面這個測試程式的輸出結果)

class ClassA  {      public static int s_value = 1;        static ClassA()      {          s_value = ClassB.s_value;      }  }    class ClassB  {      public static int s_value = 2;        static ClassB()      {          s_value = ClassA.s_value;      }  }    static void Main(string[] args)  {      Console.WriteLine(ClassA.s_value);      Console.WriteLine(ClassB.s_value);  }

解決循環依賴的一個通用方法就是抽取公用模組,讓循環依賴的模組解耦,轉而共同依賴於這個公用模組,舉例來說:

譬如 模組 A 依賴於 模組 B(需要使用 B.Func), 模組 B 也依賴於 模組 A(需要使用 A.Func),則我們抽取出 模組 C(其具有 A.Func 和 B.Func 的功能),然後讓 模組 A 和 模組 B 去除相互依賴,轉而都去依賴 模組 C。

更多資料