程序集是任何 .NET Framework 应用程序的基本构造块。例如,在生成简单的 C# 应用程序时,Visual Studio 创建一个单个可移植可执行 (PE) 文件形式的程序集,明确地说就是一个 EXE 或 DLL。
程序集包含描述它们自己的内部版本号和它们包含的所有数据和对象类型的详细信息的元数据。
程序集仅在需要时才加载。如果不使用程序集,则不会加载。这意味着程序集可能是在大型项目中管理资源的有效途径。
程序集可以包含一个或多个模块。例如,计划较大的项目时,可以让几个各个开发人员负责单独的模块,并通过组合所有这些模块来创建单个程序集。
一、程序集概述
程序集具有以下特点:
·程序集作为 .exe 或 .dll 文件实现。
·通过将程序集放在全局程序集缓存中,可在多个应用程序之间共享程序集。
·要将程序集包含在全局程序集缓存中,必须对程序集进行强命名。
·程序集仅在需要时才加载到内存中。
·可以使用反射以编程方式获取关于程序集的信息。
·如果加载程序集的目的只是对其进行检查,应使用诸如 ReflectionOnlyLoadFrom 的方法。
·可以在单个应用程序中使用相同程序集的两个版本。
二、友元程序集
可以从一个程序集访问另一个程序集中的内部类型或内部成员。
备注:
友元程序集功能用于访问内部成员;私有类型和私有成员仍然不可访问。
若要使程序集(程序集 B)能够访问另一个程序集(程序集 A)的内部类型和成员,应使用程序集 A 中的 InternalsVisibleToAttribute 属性。
说明:
在对将要访问另一个程序集(程序集 A)的内部类型或内部成员的程序集(程序集 B)进行编译时,必须用 /out 编译器选项显式指定输出文件的名称(.exe 或 .dll)。这是必需的,因为当编译器将生成的程序集绑定到外部引用时,尚未为该程序集生成名称。
StrongNameIdentityPermission 类还提供共享类型的功能,其与友元程序集的区别如下:
·StrongNameIdentityPermission 应用于单个类型,而友元程序集应用于整个程序集。
·如果希望程序集 B 能够共享程序集 A 中的数百个类型,则必须使用 StrongNameIdentityPermission 修饰所有这些类型;而使用友元程序集时只需声明一次友元关系。
·使用 StrongNameIdentityPermission 时,想要共享的类型必须声明为公共类型。使用友元程序集时,会将共享类型声明为内部类型。
·有关如何生成可访问程序集中的非公共类型的 .netmodule 的信息,请参见 /moduleassemblyname。
示例
在此示例中,程序集使内部类型和内部成员对名为 called cs_friend_assemblies_2 的程序集可见。
// cs_friend_assemblies.cs
// compile with: /target:library
using System.Runtime.CompilerServices;
using System;
[assembly:InternalsVisibleTo("cs_friend_assemblies_2")]
// internal by default
class Class1
{
public void Test()
{
Console.WriteLine("Class1.Test");
}
}
// public type with internal member
public class Class2
{
internal void Test()
{
Console.WriteLine("Class2.Test");
}
}
在此示例中,程序集使用程序集 cs_friend_assemblies.dll 中的内部类型和内部成员。
注意,必须显式指定输出文件的名称 (/out:cs_friend_assemblies_2.exe)。
如果此程序集允许另一个程序集(程序集 C)访问它的内部类型和成员,则程序集 C 不会自动变成程序集 cs_friend_assemblies.dll 的友元。
// cs_friend_assemblies_2.cs
// compile with: /reference:cs_friend_assemblies.dll /out:cs_friend_assemblies_2.exe
public class M
{
static void Main()
{
// access an internal type
Class1 a = new Class1();
a.Test();
Class2 b = new Class2();
// access an internal member of a public type
b.Test();
}
}
Class1.Test
Class2.Test
此示例演示了如何使内部类型和成员对具有强名称的程序集可用。
若要生成 keyfile 并显示公钥,请使用下面的 sn.exe 命令序列:
·sn -k friend_assemblies.snk // 生成强名称密钥
·sn -p friend_assemblies.snk key.publickey // 将公钥从 key.snk 提取到 key.publickey 中
·sn -tp key.publickey // 显示存储在文件 key.publickey 中的公钥
用 /keyfile 将 keyfile 传递到编译器。
// cs_friend_assemblies_3.cs
// compile with: /target:library /keyfile:friend_assemblies.snk
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("cs_friend_assemblies_4, PublicKey=0024000004800000940000000602000000240000525341310004000001000100031d7b6f3abc16c7de526fd67ec2926fe68ed2f9901afbc5f1b6b428bf6cd9086021a0b38b76bc340dc6ab27b65e4a593fa0e60689ac98dd71a12248ca025751d135df7b98c5f9d09172f7b62dabdd302b2a1ae688731ff3fc7a6ab9e8cf39fb73c60667e1b071ef7da5838dc009ae0119a9cbff2c581fc0f2d966b77114b2c4")]
class Class1
{
public void Test()
{
System.Console.WriteLine("Class1.Test");
}
}
此示例演示如何使用对具有强名称的程序集可用的内部类型和成员。
// cs_friend_assemblies_4.cs
// compile with: /keyfile:friend_assemblies.snk /reference:cs_friend_assemblies_3.dll /out:cs_friend_assemblies_4.exe
public class M
{
static void Main()
{
Class1 a = new Class1();
a.Test();
}
}
三、如何:与其他应用程序共享程序集
程序集可以是私有的也可以是共享的:默认情况下,大多数简单的 C# 程序都包含一个私有程序集,原因是不打算将该程序集供其他应用程序使用。
为了与其他应用程序共享程序集,必须将该程序集置于 全局程序集缓存 (GAC) 中。
共享程序集:
·创建程序集。
·为程序集指定一个强名称。
·为程序集指定版本信息。
·将您的程序集添加到全局程序集缓存中。
·从其他应用程序中访问该程序集包含的类型。
四、如何:加载和卸载程序集
程序引用的程序集将在生成时自动加载,不过也可以在运行时将特定的程序集加载到当前应用程序域中。
没有办法卸载单独的程序集而不卸载包含它的所有应用程序域。即使程序集已在范围之外,实际的程序集文件仍然保持被加载,直至包含它的所有应用程序域都被卸载。
如果想要卸载某些程序集而不卸载其他程序集,可考虑创建新的应用程序域,在该域中执行代码,然后卸载该应用程序域。
将程序集加载到应用程序域中:
·使用 AppDomain 和 System.Reflection 类中包含的几个加载方法之一。有关更多信息,请参见将程序集加载到应用程序域中。
卸载应用程序域:
·没有办法卸载单独的程序集而不卸载包含它的所有应用程序域。使用 AppDomain 中的 Unload 方法可卸载应用程序域。
五、如何:确定文件是否为程序集
当且仅当一个文件是托管文件并且在其元数据中包含程序集入口时,该文件才是一个程序集。
如何手动确定一个文件是否为程序集:
·启动 MSIL 反汇编程序 (Ildasm.exe)。
·加载希望测试的文件。
·如果 ILDASM 报告该文件不是可移植的可执行 (PE) 文件,则它不是程序集。有关更多信息,请参见主题 如何:查看程序集内容。
如何以编程方式确定一个文件是否为程序集:
·调用 GetAssemblyName 方法,并向其传递正在测试的文件的完整文件路径和名称。
·如果引发 BadImageFormatException 异常,则该文件不是程序集。
示例:
此示例测试一个 DLL 以确定它是否为程序集。
class TestAssembly
{
static void Main()
{
try
{
System.Reflection.AssemblyName testAssembly =
System.Reflection.AssemblyName.GetAssemblyName(@"C:/Windows/Microsoft.NET/Framework/v3.5/System.Net.dll");
System.Console.WriteLine("Yes, the file is an Assembly.");
}
catch (System.IO.FileNotFoundException)
{
System.Console.WriteLine("The file cannot be found.");
}
catch (System.BadImageFormatException)
{
System.Console.WriteLine("The file is not an Assembly.");
}
catch (System.IO.FileLoadException)
{
System.Console.WriteLine("The Assembly has already been loaded.");
}
}
}
/* Output (with .NET Framework 3.5 installed):
Yes, the file is an Assembly.
*/
GetAssemblyName 方法加载测试文件,然后在读取信息之后释放它。