1、如何讓Intenal成員暴露給另外一個程序集
咱們知道Modifier爲Internal的類型成員僅限於當前程序集可以訪問,可是在某些狀況下,咱們但願將它們暴露給另外一個程序集。比較典型的應用場景包括以下兩種:html
- 將一個組件或者模塊定義成兩個或者兩個以上程序集,一個程序集須要訪問另外一個程序集的Internal成員。好比將一個Logging組件定義成三個程序集:Logging.dll、Logging.Client.dll和Logging.Server.dll。其中後兩個分別用於客戶端和服務端的日誌記錄,而它們共同依賴的功能定義在Logging.dll中。定義在Logging.dll的API以共有成員的形式公佈出來,而一些僅僅須要被Logging.Client.dll和Logging.Server.dll使用的API在定義成Interna成員。
- 對一個組件或者模塊進行單元測試時候,單元測試用例須要調用定義在被測試組件或者模塊的Internal成員。
舉個例子,以下圖所示:app
我將某個組件定義在Lib項目中,而Test是與之對應的單元測試項目。定義在Lib中組建成員的可見性依賴於具體的設計,可是在不少狀況下,單元測試用例爲了儘量覆蓋較多的分支,須要調用一些Internal成員。好比,設置一些Internal屬性,或者調用一些Internal方法。ide
我在Lib中定義了以下一個表示二維向量的Vector類,其中X和Y屬性的Set方法爲Internal。工具
1: public class Vector
2: {
3: private static void EnsureNotNull(object value, string parameterName)
4: {
5: if (null == value)
6: {
7: throw new ArgumentNullException(parameterName);
8: }
9: }
10: public double X { get; internal set; }
11: public double Y { get; internal set; }
12:
13: public Vector(double x, double y)
14: {
15: this.X = x;
16: this.Y = y;
17: }
18:
19: public override bool Equals(object obj)
20: {
21: Vector vector = obj as Vector;
22: if (null == vector)
23: {
24: return false;
25: }
26:
27: return this.X == vector.X && this.Y == vector.Y;
28: }
29:
30: public static Vector operator + (Vector v1, Vector v2)
31: {
32: EnsureNotNull(v1, "v1");
33: EnsureNotNull(v2, "v2");
34: return new Vector(v1.X + v2.X, v1.Y+ v2.Y);
35: }
36:
37: public override int GetHashCode()
38: {
39: return this.X.GetHashCode() ^ this.Y.GetHashCode();
40: }
41: }
在單元測試項目Test中,定義以下一個VectorFixture類型,用於測試向量相加的邏輯。爲了測試方便,我在這裏但願直接設置Vector的X和Y屬性,而這兩個屬性的Set方式是Internal的。post
1: [TestClass()]
2: public class VectorFixture
3: {
4: [TestMethod]
5: public void Add()
6: {
7: var v1 = new Vector(1, 2);
8: var v2 = new Vector(3, 4);
9: Assert.AreEqual<Vector>(new Vector(4,6),v1+v2);
10:
11: v1.X = -1;
12: v1.Y = -2;
13: v2.X = -3;
14: v2.Y = -4;
15: Assert.AreEqual<Vector>(new Vector(-4, -6), v1 + v2);
16: }
17: }
爲了解決這個問題,我想不少人都知道一個特殊的自定義特性(Custom Attribute):System.Runtime.CompilerServices.InternalsVisibleToAttribute。沒錯,咱們只須要在Lib項目的AssemblyInfo.cs添加這個InternalsVisibleToAttribute特性,指定目標程序集(可以訪問本程序集的Internal成員的程序集)名稱便可。單元測試
1: [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Test)]
2、若是對Lib添增強簽名呢?
在不少狀況下,咱們須要將最終的程序集以強命名的形式發佈。爲此,咱們修改Lib項目設置,開啓"Sign the assembly」開關,並建立一個密鑰文件。測試
當完成上面的步驟後,Lib項目將不能經過編譯,編譯錯誤以下圖所示。具體的錯誤信息爲:「Friend assembly reference 'Test' is invalid. Strong-name signed assemblies must specify a public key in their InternalsVisibleTo declarations.」this
3、若是在InternalsVisibleToAttribute指定程序集的強名稱(Strong Name)呢?
從上面的出錯消息中咱們不難看出,編譯錯誤的緣由是:當自身具備強簽名的狀況下,經過InternalsVisibleToAttribute指定的程序集也須要具備強簽名。那麼,若是咱們將單元測試項目Test也加上強簽名,並將InternalsVisibleToAttribute特性指定成程序集的強名稱,是否能夠解決這個問題呢?spa
在對Test項目按照上面的步驟進行強簽名後,並從新修改了應用在Lib程序集上的InternalsVisibleToAttribute特性設置,即設置成包含4個部分(名稱、版本、語言文化和公鑰令牌)的程序集強名稱。命令行
1: [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8dba6a4f4e33b7dc")]
不幸的是,這會致使另外一個編譯錯誤(以下圖所示)。具體出錯信息爲:「Friend assembly reference 'Test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8dba6a4f4e33b7dc' is invalid. InternalsVisibleTo declarations cannot have a version, culture, public key token, or processor architecture specified.」。出錯信息代表:經過InternalsVisibleToAttribute特性指定的程序集名稱的時候,只能指定程序集名稱(文件名),不能指定版本、語言文化和公鑰令牌。
4、須要指定的是完整的公鑰
實際上對於上面的狀況,須要指定的不是程序名的強命名,而是指定對程序集進行簽名時採用的公鑰。那麼如何獲得這個公鑰呢?咱們能夠經過強名稱(SN.exe)命令行工具直接將公鑰從密鑰文件中提取出來。
具體來講咱們須要兩個步驟:經過SN.exe結合-p開關從將包含公鑰/私鑰的密鑰文件中提取公鑰,並導入到指定的密鑰文件中;而後執行SN.exe並結合使用-tp開關,將公鑰文件中的公鑰顯示出來。
兩個步驟地命令行輸入和輸出入下所示。其中Test.snk表示對單元測試項目進行簽名的密鑰文件,而Test.PK.snk則表示導出的只包含公鑰的密鑰文件。最終控制檯顯示出咱們須要的完整的公鑰:「0024000004800000940000000602000000240000525341310004000001000100c9d70c8b6c1eb494b113701099f43ef62efe8c9cf4310bda2061eff1cc91ffda4368848d3283d4d83e63087038e32ea25e0098891608ae48993bf16ea93362d10207de3a4dca263c145a6febf1784401948c2474c3f55713e6b97e9c1c3eef5b8966b879407b955b23404c62cd75fcf3598b6950d104a4ea97209ad051763ca4」
1: C:\Users\jinnan\Documents\Visual Studio 2010\Projects\InternalsVisibility\Test>SN -p Test.snk Test.PK.snk
2: Microsoft (R) .NET Framework Strong Name Utility Version 4.0.30319.1 Copyright (c) Microsoft Corporation. All rights reserved.
3: Public key written to Test.PK.snk
4: C:\Users\jinnan\Documents\Visual Studio 2010\Projects\InternalsVisibility\Test>SN -tp Test.PK.snk
5: Microsoft (R) .NET Framework Strong Name Utility Version 4.0.30319.1 Copyright (c) Microsoft Corporation. All rights reserved.
6: Public key is: 0024000004800000940000000602000000240000525341310004000001000100c9d70c8b6c1eb494b113701099f43ef62efe8c9cf4310bda2061eff1cc
7: 91ffda4368848d3283d4d83e63087038e32ea25e0098891608ae48993bf16ea93362d10207de3a4dca263c145a6febf1784401948c2474c3
8: f55713e6b97e9c1c3eef5b8966b879407b955b23404c62cd75fcf3598b6950d104a4ea97209ad051763ca4
9: Public key token is 8dba6a4f4e33b7dc
咱們只須要將該公鑰指定到InternalsVisibleToAttribute特性中便可:
1: [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Test,PublicKey=0024000004800000940000000602000000240000525341310004000
2: 001000100c9d70c8b6c1eb494b113701099f43ef62efe8c9cf4310bda2061eff1cc91ffda4368848d3283d4d83e63087038e32ea25e0098891608ae48993bf16ea933
3: 62d10207de3a4dca263c145a6febf1784401948c2474c3f55713e6b97e9c1c3eef5b8966b879407b955b23404c62cd75fcf3598b6950d104a4ea97209ad051763ca4")]