依賴注入框架Castle Windsor從容器裡解析一個實例時(也就是調用Resolve方法),是通過調用待解析對象的構造函數new一個對象並返回,那麼問題是:它是調用哪個構造函數呢?
帶著這個問題,我寫了一段測試代碼.
測試1:
只有一個無參構造函數:
CtorTest類(在控制台程序裡用Windsor解析這個類)
public class CtorTest { public string Message { get; set; } public CtorTest() { Message = $"The message is from {nameof(CtorTest)}"; } public void ShowMessage() { Console.WriteLine(Message); } }
控制台Main代碼如下所示:
class Program { static void Main(string[] args) { IWindsorContainer iocContainer = new WindsorContainer(); iocContainer.Register(Component.For<CtorTest>().ImplementedBy<CtorTest>().LifestyleSingleton()); var instance = iocContainer.Resolve<CtorTest>(); instance.ShowMessage(); } }
測試結果(默認構造函數與無參構造函數性質是一樣的):
測試2
只有一個帶參但不是靠依賴注入的構造函數(沒有無參數構造函數)
CtorTest代碼如下:
public string Message { get; set; } public CtorTest(string message) { Message = $"The message is from {nameof(CtorTest)}"; } public void ShowMessage() { Console.WriteLine(Message); } }
測試結果,當然是拋出異常:
如果為這個參數提供默認值(如:string message=""),Resolve會調用這個構造函數,如果再加一個無參構造函數,Resolve會調用帶參的,如再加一個帶有兩個帶默認值的帶參構造函數,則會調用兩個參數的,所以這裡的結論是:先帶有默認值的有參(先參數個數多的),再無參.
測試3:
有一個帶參且參數是靠依賴注入的構造函數,和一個無參數構造函數,一個兩個具有默認值參數的構造函數.
添加一個Sub類:
public class Sub { public string Message { get; set; } public Sub() { Message = $"The message is from {nameof(Sub)}"; } }
Ctor類代碼如下:
public class CtorTest { public string Message { get; set; } public CtorTest() { Message = $"The message is from {nameof(CtorTest)}"; }
public CtorTest(string message = "message1",string message2= "message2")
{
Message = $"The message is from {nameof(CtorTest)} and {message} and {message2}" ;
}
public CtorTest(Sub sub) { Message = sub.Message; } public CtorTest(string message = "") { Message = $"The message is from {nameof(CtorTest)}"; } public void ShowMessage() { Console.WriteLine(Message); } }
Main如下:
class Program { static void Main(string[] args) { IWindsorContainer iocContainer = new WindsorContainer(); iocContainer.Register(Component.For<CtorTest>().ImplementedBy<CtorTest>().LifestyleSingleton());
//把sub注入到容器中 iocContainer.Register(Component.For<Sub>().ImplementedBy<Sub>().LifestyleSingleton()); var instance = iocContainer.Resolve<CtorTest>(); instance.ShowMessage(); } }
測試結果:
從結果可以看出它是通過帶參(參數是依賴注入)的構造函數創建實例,即使在有一個2個具有默認值的參數的構造函數的情況下.
測試4
兩個帶參且參數是靠依賴注入的構造函數
添加一個Sub2類:
public class Sub2 { public string Message { get; set; } public Sub2() { Message = $"The message is from {nameof(Sub2)}"; } }
Ctor類代碼如下:
public class CtorTest { public string Message { get; set; } public CtorTest() { Message = $"The message is from {nameof(CtorTest)}"; } //注意:我故意把這個放到sub參數的構造函數前面 public CtorTest(Sub2 sub2) { Message = sub2.Message; } public CtorTest(Sub sub) { Message = sub.Message; } public CtorTest(string message = "") { Message = $"The message is from {nameof(CtorTest)}"; } public void ShowMessage() { Console.WriteLine(Message); } }
Main類代碼如下:
class Program { static void Main(string[] args) { IWindsorContainer iocContainer = new WindsorContainer(); iocContainer.Register(Component.For<CtorTest>().ImplementedBy<CtorTest>().LifestyleSingleton());
//把sub2注入到容器中,注意我故意把sub2放到sub前面 iocContainer.Register(Component.For<Sub2>().ImplementedBy<Sub2>().LifestyleSingleton()); //把sub注入到容器中 iocContainer.Register(Component.For<Sub>().ImplementedBy<Sub>().LifestyleSingleton()); var instance = iocContainer.Resolve<CtorTest>(); instance.ShowMessage(); } }
測試結果:
盡管我把Sub2的構造函數和注冊都放在了Sub前面,但最終還是調用了帶Sub參數的構造函數.那麼它的順序是什麼呢?通過修改類的名稱(比如說把Sub2改成排序在Sub前的名稱,如S,那麼就會調用S這個參數的構造函數)
測試5
有兩個帶參且參數是靠依賴注入的構造函數
把CtorTest類裡的
public CtorTest(Sub2 sub2) { Message = sub2.Message; }
修改成
public CtorTest(Sub2 sub2,Sub sub) { Message = sub2.Message +Environment.NewLine + sub.Message; }
測試結果:
它調用的是修改後的這個構造函數,也就是說:它先調用了參數多的那個.
最終總終:
Resolve先調用參數個數多且參數通過依賴注入的構造函數,如果參數個數相同的構造函數有多個,則按參數類型名稱(這個名稱應該是完全限定名,沒有測試)順序,調用第一個,如果不存在這樣的構造函數,則優先調用參數個數多且具有默認值的構造函數.