zoukankan      html  css  js  c++  java
  • 使用InternalsVisibleToAttribute给assembly添加“友元assembly”特性遭遇"强签名"

    一、如何让Intenal成员暴露给另一个程序集

    我们知道Modifier为Internal的类型成员仅限于当前程序集能够访问,但是在某些情况下,我们希望将它们暴露给另一个程序集。比较典型的应用场景包括如下两种:

    • 将一个组件或者模块定义成两个或者两个以上程序集,一个程序集需要访问另一个程序集的Internal成员。比如将一个Logging组件定义成三个程序集:Logging.dll、Logging.Client.dll和Logging.Server.dll。其中后两个分别用于客户端和服务端的日志记录,而它们共同依赖的功能定义在Logging.dll中。定义在Logging.dll的API以共有成员的形式公布出来,而一些仅仅需要被Logging.Client.dll和Logging.Server.dll使用的API在定义成Interna成员。
    • 对一个组件或者模块进行单元测试时候,单元测试用例需要调用定义在被测试组件或者模块的Internal成员

    举个例子,如下图所示:

    我将某个组件定义在Lib项目中,而Test是与之对应的单元测试项目。定义在Lib中组建成员的可见性依赖于具体的设计,但是在很多情况下,单元测试用例为了尽可能覆盖较多的分支,需要调用一些Internal成员。比如,设置一些Internal属性,或者调用一些Internal方法。

    我在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的

       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)]

    二、如果对Lib添加强签名呢?

    在很多情况下,我们需要将最终的程序集以强命名的形式发布。为此,我们修改Lib项目设置,开启"Sign the assembly”开关,并创建一个密钥文件。

    image

    当完成上面的步骤后,Lib项目将不能通过编译,编译错误如下图所示。具体的错误信息为:“Friend assembly reference 'Test' is invalid. Strong-name signed assemblies must specify a public key in their InternalsVisibleTo declarations.

    image

    三、如果在InternalsVisibleToAttribute指定程序集的强名称(Strong Name)呢?

    从上面的出错消息中我们不难看出,编译错误的原因是:当自身具有强签名的情况下,通过InternalsVisibleToAttribute指定的程序集也需要具有强签名。那么,如果我们将单元测试项目Test也加上强签名,并将InternalsVisibleToAttribute特性指定成程序集的强名称,是否可以解决这个问题呢?

    在对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特性指定的程序集名称的时候,只能指定程序集名称(文件名),不能指定版本、语言文化和公钥令牌

     
    image

    四、需要指定的是完整的公钥

    实际上对于上面的情况,需要指定的不是程序名的强命名,而是指定对程序集进行签名时采用的公钥。那么如何得到这个公钥呢?我们可以通过强名称(SN.exe)命令行工具直接将公钥从密钥文件中提取出来。

    具体来说我们需要两个步骤:通过SN.exe结合-p开关从将包含公钥/私钥的密钥文件中提取公钥,并导入到指定的密钥文件中;然后执行SN.exe并结合使用-tp开关,将公钥文件中的公钥显示出来。

    两个步骤地命令行输入和输出入下所示。其中Test.snk表示对单元测试项目进行签名的密钥文件,而Test.PK.snk则表示导出的只包含公钥的密钥文件。最终控制台显示出我们需要的完整的公钥:“0024000004800000940000000602000000240000525341310004000001000100c9d70c8b6c1eb494b113701099f43ef62efe8c9cf4310bda2061eff1cc91ffda4368848d3283d4d83e63087038e32ea25e0098891608ae48993bf16ea93362d10207de3a4dca263c145a6febf1784401948c2474c3f55713e6b97e9c1c3eef5b8966b879407b955b23404c62cd75fcf3598b6950d104a4ea97209ad051763ca4

    1: C:UsersjinnanDocumentsVisual Studio 2010ProjectsInternalsVisibilityTest>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:UsersjinnanDocumentsVisual Studio 2010ProjectsInternalsVisibilityTest>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")]

    出处:https://www.cnblogs.com/artech/archive/2010/10/06/1844721.html

    ====================================================================

    C#的internal关键字可以使标记的方法,字段或者属性等等只能在当前assembly内部使用,那么如果其他的assembly需要使用这个internal的方法的时候怎么办呢?.NET提供了一种类似于C++中的友元类的方式来完成这个功能,那就是使用InternalsVisibleTo

    这种情况常见于做测试的时候,需要另外一个项目来测试项目中的internal方法所标记的功能,所以有了InternalsVisibleTo,我们就不用为了做单元测试而把一个本不该公开的方法改为public了.

    使用InternalsVisibleTo还有一些需要注意的地方,特别是PublicKey不太容易弄的明白,下面先来说说这个InternalsVisibleTo该怎么使用:

    先来说明一下前提:Project1是功能项目,Project1.Test (assembly name: Project1.Test.dll)是为做Project1的测试工程。

    1. 打开Project1的Assembly.cs文件,在文件的末尾加上这样一句话:

      [assembly: InternalsVisibleTo("Project1.Test, PublicKey=******")]

    其中PublicKey=******应该替换成Project1.Test.dll的public key,至于如何获取PublicKey,请看文章末尾的Notes部分.

    2. 确认namespace: System.Runtime.CompilerServices 添加到了Assembly.cs的namespace引用中,因为InternalsVisibleTo位于命名空间System.Runtime.CompilerService中。

    Notes:

    1. 如何获取PublicKey?

    A: 在命令行下,使用sn -Tp Project1.Test.dll就可以看到Public  Key  is ......和Public Key Token is ......

    2. 如果Project1是个strong-named的项目,那么InternalsVisibleTo必须指定PublicKey,所以Project1.Test也必须使用强签名才能正确使用InternalsVisibleTo, 不然编译会出错,如果Project1没有使用强签名,那么Project1.Test也不必使用强签名,而且在使用InternalsVisibleTo的时候只需要程序集的名字就可以了,不需要设置PuklicKey。

    出处:https://www.cnblogs.com/lmule/archive/2010/08/15/1800227.html

  • 相关阅读:
    Matlab P文件——加快Matlab程序,保护你的算法
    电烙铁的使用
    CV、PR方向的资源
    遗传算法初接触
    Linux进程控制
    C语言编程优化运行速度
    用MATLAB优化工具箱解线性规划
    50、linux shell命令,netstat,traceroute
    51、linux shell命令,route,ifconfig
    56、vi常见用法,多窗口模式,标记,多文件编辑,快捷操作及设置
  • 原文地址:https://www.cnblogs.com/mq0036/p/9660746.html
Copyright © 2011-2022 走看看