Assembly.Load

来源:互联网 发布:nodejs 书籍 知乎 编辑:程序博客网 时间:2024/06/05 04:28

Assembly.Load("程序集名");Assembly.LoadFrom("程序集实际路径");

反射Assembly.Load("程序集").CreateInstance("命名空间.类")

在编程中,使用反射(IoC)是一个很好的架构。在.Net中,System.Reflection命名空间提供了对反射的支持。然而,很多朋友在使用Assembly.Load()方法时, 却不能正确装载程序集。比如,很多朋友在模仿PetShop的框架时,使用这样的调用方式:

string assemblyName = ConfigurationManager.AppSettings["webDAL"];
string constructor = ConfigurationManager.AppSettings["constructorClass"];
 return (IExample)Assembly.Load(assemblyName).CreateInstance(constructor, false);

然而,在Assembly.Load()方法处,经常出现未能加载程序集的错误:

未能加载文件或程序集“webDAL”或它的某一个依赖项。系统找不到指定的文件

Assembly.Load(assemblyName)实际上是在assemblyName.dll文件中查找类custructor的定义。例如,Assembly.Load("PetShop.SQLServerDAL").CreateInstance("PetShop.SQLServerDAL.Cateogry"),就是在PetShop.SQLServerDAL.dll程序集中查找PetShop.SQLServerDAL.Category类。而在自己定义类库时,往往忽视了生成的程序集的名称。

在类库项目上点击右键->属性,可以设定生成的程序集的文件名。只有正确设置了,才能在Assembly.Load(assemblyName)方法中避免找不到程序集的错误。


一.Load方法
1.Load(AssemblyName) 在给定程序集的 AssemblyName 的情况下,加载程序集。
例1: TestLib.LzdTest lzdTeset = (TestLib.LzdTest)Assembly.Load("TestLib").CreateInstance("TestLib.LzdTest");


用Reflector查看源码如下:
public static Assembly Load(AssemblyName assemblyRef)
{
    StackCrawlMark lookForMyCaller = StackCrawlMark.LookForMyCaller;
    return RuntimeAssembly.InternalLoadAssemblyName(assemblyRef, null, ref lookForMyCaller, false, false);
}

 
2.Load(AssemblyString) 通过给定程序集的长格式名称加载程序集。
例2: TestLib.LzdTest lzdTeset = (TestLib.LzdTest)Assembly.Load("TestLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null").CreateInstance("TestLib.LzdTest");


用Reflector查看源码如下:
public static Assembly Load(string assemblyString)
{
    StackCrawlMark lookForMyCaller = StackCrawlMark.LookForMyCaller;
    return RuntimeAssembly.InternalLoad(assemblyString, null, ref lookForMyCaller, false);
}


[MethodImpl(MethodImplOptions.NoInlining), SecurityCritical]
internal static RuntimeAssembly InternalLoad(string assemblyString, Evidence assemblySecurity, ref StackCrawlMark stackMark, bool forIntrospection)
{
    if (assemblyString == null)
    {
        throw new ArgumentNullException("assemblyString");
    }
    if ((assemblyString.Length == 0) || (assemblyString[0] == '\0'))
    {
        throw new ArgumentException(Environment.GetResourceString("Format_StringZeroLength"));
    }
    AssemblyName assemblyRef = new AssemblyName();
    RuntimeAssembly assembly = null;
    assemblyRef.Name = assemblyString;
    if (assemblyRef.nInit(out assembly, forIntrospection, true) == -2146234297)
    {
        return assembly;
    }
    return InternalLoadAssemblyName(assemblyRef, assemblySecurity, ref stackMark, forIntrospection, false);
}

总结: 使用Load方法加载程序集,特别是强命名程序集,能在程序集上应用安全和部署策略,一般情况下都应该优先使用这个方法。


二.LoadFrom (已知程序集的文件名或路径,加载程序集。)
例3: TestLib.LzdTest lzdTeset = (TestLib.LzdTest)Assembly.LoadFrom("D:\\workspace\\LatestWebTestSite\\TestLib.dll").CreateInstance("TestLib.LzdTest");


用Reflector查看源码如下:
public static Assembly LoadFrom(string assemblyFile)
{
    StackCrawlMark lookForMyCaller = StackCrawlMark.LookForMyCaller;
    return RuntimeAssembly.InternalLoadFrom(assemblyFile, null, null, AssemblyHashAlgorithm.None, false, false, ref lookForMyCaller);
}

 
[MethodImpl(MethodImplOptions.NoInlining), SecurityCritical]
internal static RuntimeAssembly InternalLoadFrom(string assemblyFile, Evidence securityEvidence, byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm, bool forIntrospection, bool suppressSecurityChecks, ref StackCrawlMark stackMark)
{
    if (assemblyFile == null)
    {
        throw new ArgumentNullException("assemblyFile");
    }
    if ((securityEvidence != null) && !AppDomain.CurrentDomain.IsLegacyCasPolicyEnabled)
    {
        throw new NotSupportedException(Environment.GetResourceString("NotSupported_RequiresCasPolicyImplicit"));
    }
    AssemblyName assemblyRef = new AssemblyName();
    assemblyRef.CodeBase = assemblyFile;
    assemblyRef.SetHashControl(hashValue, hashAlgorithm);
    return InternalLoadAssemblyName(assemblyRef, securityEvidence, ref stackMark, forIntrospection, suppressSecurityChecks);
}

MSDN: LoadFrom 方法具有以下缺点。请考虑改用 Load。
1.如果已加载一个具有相同标识的程序集,则即使指定了不同的路径,LoadFrom 仍返回已加载的程序集。
2.如果用 LoadFrom 加载一个程序集,随后加载上下文中的一个程序集尝试加载具有相同显示名称的程序集,则加载尝试将失败。对程序集进行反序列化时,可能发生这种情况。

总结: LoadFrom只能用于加载不同标识的程序集, 也就是唯一的程序集, 不能用于加载标识相同但路径不同的程序集。

从例子1,2,3的源代码中可以看出来, 三者归根结都是调用RuntimeAssembly.InternalLoadAssemblyName()方法的.

 

三.LoadFile (加载指定路径上的程序集文件的内容。)
例4: TestLib.LzdTest lzdTeset = (TestLib.LzdTest)Assembly.LoadFrom("D:\\workspace\\LatestWebTestSite\\TestLib.dll").CreateInstance("TestLib.LzdTest");

用Reflector查看源码如下:
public static Assembly LoadFile(string path)
{
    new FileIOPermission(FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, path).Demand();
    return RuntimeAssembly.nLoadFile(path, null);
}

[MethodImpl(MethodImplOptions.InternalCall), SecurityCritical]
internal static extern RuntimeAssembly nLoadFile(string path, Evidence evidence);
 
extern 修饰符用于声明 由程序集外部实现的成员函数经常用于系统API函数的调用(通过 DllImport )。
注意,和DllImport一 起使用时要加上 static 修饰符也可以用于对于同一程序集不同版本组件的调用(用 extern 声明别名) 不能与 abstract 修饰符同时使用 。


这个方法也不能加载同标识不同路径的程序集,否则也提示错误:

[A]TestLib.LzdTest cannot be cast to [B]TestLib.LzdTest. Type A originates from 'TestLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadNeither' at location 'D:\workspace\LatestWebTestSite\LatestWebTestSite\bin\TestLib.dll'. Type B originates from 'TestLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\25a52d02\3d6c3f29\assembly\dl3\c37d3dd1\72d288f9_135bcc01\TestLib.DLL'.

 

总结: 这个方法是从指定的文件来加载程序集,它是调用外部的API实现的加载方式,和上面Load,LoadFrom方法的不同之处是这个方法不会加载此程序集引用的其他程序集,也就是不会加载程序的依赖项。


1、Assembly.LoadFile只载入相应的dll文件,比如Assembly.LoadFile("a.dll"),则载入a.dll,假如a.dll中引用了b.dll的话,b.dll并不会被载入。
Assembly.LoadFrom则不一样,它会载入dll文件及其引用的其他dll,比如上面的例子,b.dll也会被载入。
2、用Assembly.LoadFrom载入一个Assembly时,会先检查前面是否已经载入过相同名字的Assembly,比如a.dll有两个版本(版本1在目录1下,版本2放在目录2下),程序一开始时载入了版本1,当使用Assembly.LoadFrom("2\\a.dll")载入版本2时,不能载入,而是返回版本1。
Assembly.LoadFile的话则不会做这样的检查,比如上面的例子换成Assembly.LoadFile的话,则能正确载入版本2。