C#工作總結(四):迴圈引用的思考
一. 引子(Introduction)
大概在半年前,曾與公司某個同事爭吵一個迴圈引用的問題。當時吵的面紅耳赤,這幾天想把當時,為了吵贏那次問題,做的功課給寫下來。
然後是,以前公司部門經理吹噓某部門大佬在移交客戶程式碼中解決迴圈引用的問題圖鎮樓。我就不懂為什麼他寫的這樣的程式碼都不會被客戶反饋,而我寫程式碼就算自己沒寫錯,標準版也會報一堆錯誤,真的是太悲傷了。
我其實一直想不通的是,為什麼能寫出這樣程式碼的人,不但每次在我被批評的時候,都拿出來講成榜樣給我聽,A數C數太多了。
話不多說,來看看的如何解決。
二. 目錄(Contents)
1. 引入場景
2. 使用委託解決
3. 使用反射解決
4. 使用介面解決
5. 示例工程下載
三. 場景(Situation)
在解決問題之前,我先引入一個場景。假設有一個類A,它裡面有兩個方法一個是MethodA_One()而另外一個是MethodA_Two()。於此同時,有另外一個類B,它裡面有兩個方法MethodB_One()個MethodB_Two()。
遇到這樣一個業務場景類A中的MethodA_One()要呼叫類B中的MethodB_One(),而類B中的MethodB_Two()要呼叫A中的MethodA_Two()中的方法。
明顯可知,如果類A和類B分別在兩個程式集中,如果彼此引用會出現迴圈引用的問題。那麼下面的解決方法都以這個列子進行處理。
四. 使用委託解決(Using Delegate)
一般人遇到這個問題的第一反應就是委託。因為委託很像“傳遞一個函式指標”給另一個類,往往在類與類之間可以不通過直接的物件引用,來使用另一個類的方法。
首先讓A的程式集引用B的程式集。
(1)類A程式碼:
public class A { private B innerClassB = null; public A(B classB) { classB.MethodDelegate = (Action)this.MethodA_Two; this.innerClassB = classB; } public void MethodA_One() { Console.WriteLine("Class A: MethodA_One"); if(this.innerClassB!=null) { innerClassB.MethodB_One(); } } public void MethodA_Two() { Console.WriteLine("Class A: MethodA_Two"); } }
(2)類B的程式碼:
public class B
{
public Action MethodDelegate = null;
public void MethodB_One()
{
Console.WriteLine("Class B: MethodB_One");
}
public void MethodB_Two()
{
Console.WriteLine("Class B: MethodB_Two");
if(this.MethodDelegate!=null)
{
MethodDelegate();
}
}
}
(3)上端呼叫:
B classB = new B();
A classA = new A(classB);
classA.MethodA_One();
classB.MethodB_Two();
五. 使用反射解決(Using Reflection)
反射也不失為一個很好的方法。在一些專案中常用反射來進行解耦(解除類與類之間中引用)。當然使用反射的話,不但會造成一定的效能的損失,而且會讓程式碼變的更加的難懂(本來簡單的程式碼變得複雜了)的。
首先讓A的程式集引用B的程式集。
(1)在B的程式集中增加介面IMethod:
public interface IMethod
{
void MethodA_One();
void MethodA_Two();
}
(2)類A程式碼:
public class A : IMethod
{
private B innerClassB = null;
public A(B classB)
{
this.innerClassB = classB;
}
public void MethodA_One()
{
Console.WriteLine("Class A: MethodA_One");
if (innerClassB != null)
{
innerClassB.MethodB_One();
}
}
public void MethodA_Two()
{
Console.WriteLine("Class A: MethodA_Two");
}
}
(3)類B程式碼:
public class B
{
public void MethodB_One()
{
Console.WriteLine("Class B: MethodB_One");
}
public void MethodB_Two()
{
Console.WriteLine("Class B: MethodB_Two");
string path = AppDomain.CurrentDomain.BaseDirectory + "ClassALib.dll";
Assembly assembly = Assembly.LoadFrom(path);
IMethod classA = null;
if (assembly!=null)
{
Type type = assembly.GetType("ClassALib.A");
if (type != null)
{
classA = Activator.CreateInstance(type, this) as IMethod;
}
}
if(classA!=null)
{
classA.MethodA_Two();
}
}
}
(4)上端呼叫:
B classB = new B();
A classA = new A(classB);
classA.MethodA_One();
classB.MethodB_Two();
六. 使用介面解決(Using Interface)
這個是比較簡單而且是比較推薦的做法。
首先讓A的程式集引用B的程式集。
(1)在B的程式集中增加介面IMethod:
public interface IMthod
{
void MethodA_Two();
}
(2)類A程式碼:
public class A:IMthod
{
private B innerClassB = null;
public void GetClassB(B classB)
{
this.innerClassB = classB;
}
public void MethodA_One()
{
Console.WriteLine("Class A: MethodA_One");
if(innerClassB!=null)
{
innerClassB.MethodB_One();
}
}
public void MethodA_Two()
{
Console.WriteLine("Class A: MethodA_Two");
}
}
(3)類B程式碼:
public class B
{
private IMthod innerClassA = null;
public B(IMthod classA)
{
this.innerClassA = classA;
}
public void MethodB_One()
{
Console.WriteLine("Class B: MethodB_One");
}
public void MethodB_Two()
{
Console.WriteLine("Class B: MethodB_One");
if(innerClassA!=null)
{
innerClassA.MethodA_Two();
}
}
}
(4)上端呼叫:
A classA = new A();
B classB = new B(classA);
classA.GetClassB(classB);
classA.MethodA_One();
classB.MethodB_Two();
七. 示例工程下載(Download Project)