动态反射——Load,LoadFrom和LoadFile

【问】

假设有一个类库文件LibraryA,其中有一个ClassA,该类的AssemblyName为“LibraryA”(编译后的文件是LibraryA.dll)。另外有一个LibraryB.dll类库文件,其中AssemblyName和其命名空间一样,并且其引用LibraryA.dll。它们代码如下:

[C#]

【LibraryA.dll】

namespace A
{
public class ClassA
{
public ClassA()
{
Console.WriteLine("成功执行ClassA的构造函数。");
}
}
}

【LibraryB.dll】

using LibraryA;

namespace LibraryB
{
public class ClassB
{
public ClassA GetClassA()
{
return new ClassA();
}

public ClassB()
{
Console.WriteLine("成功执行ClassB的构造函数。");
}
}
}

[VB.NET]

【LibraryA.dll】

Namespace A
Public Class ClassA
Public Sub New()
Console.WriteLine("成功执行ClassA的构造函数。")
End Sub
End Class
End Namespace

【LibraryB.dll】

Imports LibraryA

Namespace LibraryB
Public Class ClassB
Public Function GetClassA() As ClassA
Return New ClassA()
End Function

Public Sub New()
Console.WriteLine("成功执行ClassB的构造函数。")
End Sub
End Class
End Namespace

现在假设有一个控制台程序(包含Main主函数,该控制台类也已经引用了LibraryA)。要求:

       1、设法使用反射方法运行LibraryA的构造函数。

       2、设法使用动态加载方法(只允许加载LibraryB.dll,假设位于D:\目录下),运行GetClassA方法。

       假设在D:\ 下有两个文件夹(f1和f2)。在f1中复制原本LibraryB.dll一份;然后在f2复制修改过的LibraryB.dll(就是构造函数输出变更为“成功执行ClassB的构造函数更新版”,然后重新编译拷贝进入f2)。我在控制台主程序中动态加载两个不同路径,但是相同版本的LibraryB.dll。请问以下程序运行之后结果是什么?

[C#]

Assembly asm = Assembly.LoadFrom(@“D:\f1\LibraryB.dll”);
asm.CreateInstance(“LibraryB.ClassB”);
asm = Assembly.LoadFrom (@“D:\f2\LibraryB.dll”);
asm.CreateInstance(“LibraryB.ClassB”);

[VB.NET]

Dim asm As Assembly = Assembly.LoadFrom (@“D:\f1\LibraryB.dll”)
asm.CreateInstance(“LibraryB.ClassB”)
asm = Assembly. LoadFrom (@“D:\f2\LibraryB.dll”)
asm.CreateInstance(“LibraryB.ClassB”)

【错误回答】

 1)

[C#]

Assembly asm = Assembly.Load(“A”);
asm.CreateInstance(“A.ClassA”);

[VB.NET]

Dim asm As Assembly = Assembly.Load(“A”)
asm.CreateInstance(“A.ClassA”)

理由:Load需要一个AssemblyName,也就是Namespace的名称。然后后面的CreateInstance需要一个“命名空间名.类名”。

2)

[C#]

Assembly asm = Assembly.LoadFile(@“D:\LibraryB.dll”);
asm.CreateInstance(“LibraryB.ClassB”).GetType().GetMethod(“GetClassA”);

[VB.NET]

Dim asm As Assembly = Assembly.LoadFile(“D:\LibraryB.dll”)
asm.CreateInstance(“LibraryB.ClassB”).GetType().GetMethod(“GetClassA”)

3)输出两行话——

成功执行ClassB的构造函数。

成功执行ClassB的构造函数更新版。

【正解】

第一问:初学者似乎没有区分“AssemblyName”、“Namespace”的区别——的确,很少有教师去教他们区别这两者之间的差异。AssemblyName是“程序集”名字,是用于记录程序信息的一种特殊唯一标识符,供.NET的底层CLR调用识别的;Namespace是面向程序设计者,使得客户可以将不同的类进行归档,因此Namespace类似文件夹,而每一个Class类似于文件夹中的文件;至于Assembly相当于“硬盘的卷标”作用。

因此,Assembly.Load的第一个参数需要的是AssemblyName,和Namespace并无直接联系。你完全可以根据需要修改AssemblyName(方法:右键项目,选择“属性”,在“程序(Application)”面板上就可以看到)

第二问和第三问其实是考察如何正确使用LoadFile和LoadFrom动态加载指定的类库。

第二问:错误代码将导致一个“无法找到加载文件”的类似错误,其原因是在于LibraryB.dll引用了LibraryA.dll,但是如果使用了LoadFile,则动态反射LibraryB.dll的时候不会自动加载程序集LibraryA.dll,但是“GetClassA”方法却需要使用到LibraryA中的类。继而引发错误。而要使得被引用的其它dll也一并加载进来,应该使用LoadFrom方法

第三问和第二问相反,考察LoadFile和LoadFrom加载一个程序集相同,但是处于不同位置的类库(注意:这里“程序集相同”是指在源代码基础上略作修改,形成副本的dll)。

第三问:LoadFile只管加载类库,只要指定了绝对或者相对屋里路径的类库,其总是加载并以此为准;但是LoadFrom不同——如果遇到了相同的程序集的类库文件,以第一次的加载为准。所以输出的两句话都是“成功执行ClassB的构造函数。”。

【总结】

1 AssemblyNameNamespace不是一回事情,AssemblyName是供.NET使用的;而Namespace是客户自定义的“归档”类的命名空间。Assembly.Load需要前者,但是刚创建项目时,VS默认情况下两者一致。

2LoadFile只管加载程序集文件,但是反射仅限于当前程序集自身中的方法、属性等,如果需要反射引用到的外部程序集,必须使用LoadFrom

3LoadFrom对于相同程序集文件只加载第一次,因此只返回第一次的结果;要反射不同路径但是相同的程序集文件,必须使用LoadFile

【拓展】

“程序集”是包含一个或者多个类型定义文件和资源文件的集合,一般地当创建了一个完整的.NET程序(无论是C#或者是VB.NET的),VS默认就为其产生一个程序集。程序集一般包含程序的若干信息(比如程序的版本号等),它们都是存储在一个叫做“Assembly.cs”(VB.NET中是AssemblyInfo.vb)的文件中。如果双击打开该文件,我们便可窥知一二(注释删除)——

[C#]

[assembly: AssemblyTitle("CSharp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft IT")]
[assembly: AssemblyProduct("CSharp")]
[assembly: AssemblyCopyright("Copyright © Microsoft IT 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

[VB.NET]

<Assembly: AssemblyTitle("CSharp")>
<Assembly: AssemblyDescription("")>
<Assembly: AssemblyConfiguration("")>
<Assembly: AssemblyCompany("Microsoft IT")>
<Assembly: AssemblyProduct("CSharp")>
<Assembly: AssemblyCopyright("Copyright © Microsoft IT 2011")>
<Assembly: AssemblyTrademark("")>
<Assembly: AssemblyCulture("")>
<Assembly: AssemblyVersion("1.0.0.0")>
<Assembly: AssemblyFileVersion("1.0.0.0")>

下面来说说如何读取这些数据:

1)AssemblyVersion是程序的版本号。一般地,通过读取程序的版本号可以了解程序是否处于最新状态,不少更新程序往往就通过类似手段或者方式更新程序的。要读取一个当前程序的版本号,我们通过Assembly.GetName方法获取Version属性即可。

[C#]

AssemblyName an = Assembly.GetExecutingAssembly().GetName();
Console.WriteLine(an.Version);

[VB.NET]

Dim an As AssemblyName = Assembly.GetExecutingAssembly().GetName()
Console.WriteLine(an.Version)

GetExecutingAssembly是一个获取当前正运行的程序的Assembly实体。如果要读取指定某个程序的版本号,我们可以使用Assembly.LoadFile或者Assembly.LoadFrom加载一个dll或者是exe文件,然后通过GetName获取AssemblyName,再按照上面相同的方法通过诸如Version一类的属性就可以了。

顺便说一下“AssemblyName”。通过“拓展”之前的分析就可以其是一个“程序集”名称。实际上.NET把它定义成一个类,专门可以用于获取Culture(对应AssemblyCulture),Name(就是指当前程序的“程序集名称”)和Version(指代AssemblyVersion)等信息其中Version还包括“Major”(主版本号)、“Minor”(次版本号)、“Build”(编译版本号)和“Revision”(修正版本号)。它们作用分别是:

1)  Major(主板本号)+Minor(次版本号):两个合成用于对外发布,对外告知客户目前程序的版本。一般地,Major表示“里程碑”式的程序更新(比如Windows98XP,那么Major就会产生影响,自增1);而Minor是在当前程序中进行的局部重大更新(比如在原来程序基础上增删了功能,或者发现了漏洞进行修补等)时用到。

2)  Build(编译版本号):内部告知开发人员或者测试人员,目前该程序从开始编译到全部完成,总共编译的次数(每重新编译一次此自增1)。

3)  Revision(修正版本号):每当有一个Bug在内测时被发现,此版本号在原来基础上加1,表示总共从开始到完成历经多少了Bug修复。

AssemblyVersion和AssemblyFileVersion一般需要保持一致。前者是被.NET内部反射使用到的,后者是对外,可以通过右键=>属性中查看得到。比如查看Word2010程序我们可以了解以下信息:

                     

第一个“14”表示Word家族已经历经到目前开发了14个不同的“里程碑”式版本,第二个“0”表示目前为止尚未发现在office2010中有明显增加或是删除功能;“5123”表示该版本的Word程序总共编译了5123次,而“5000”表示从开始到结束总共修正了5000个Bug。

除了可以在Assembly.cs中看到文件信息,我们同样可以通过右键某个csproj(VB.NET中是vbproj),“属性”=>“应用程序(Application)”中得到相同的信息:

默认主版本号是1,其余都是0;下方的一个“复选框”是“自动为Revision版本号自增1”;也就是说,每次对外发布时,该程序的Revision会在原来基础上加上1。

2)除了版本号之外,其余的Assembly信息(比如AssemblyTitle)可以通过这样的方式获得:

[C#]

AssemblyTitleAttribute at = (AssemblyTitleAttribute) AssemblyTitle.GetCultureAttribute(
Assembly.GetExecutingAssembly(),typeof(AssemblyTitle)
);
Console.WriteLine(at.Title);

[VB.NET]

   Dim at As AssemblyTitleAttribute = DirectCast(
AssemblyTitleAttribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), GetType(AssemblyTitleAttribute)
), AssemblyTitleAttribute) Console.WriteLine(at.Title)


更一般地,因为Assembly文件中这些都是“特性”类(省略了后缀Attribute)。因此我们可以使用反射的方法获取它们,伪代码描述如下:

[C#]

XXXAttribute 变量名= XXXAttribute.GetCustomAttribute(Assembly实体对象,typeof(XXXAttribute));

变量名.属性;

[VB.NET]

Dim 变量名 As XXXAttribute== XXXAttribute.GetCustomAttribute(Assembly实体对象,typeof(XXXAttribute))

变量名.属性

几点说明:

1)  XXX”表示对应Assembly文件中的“assembly:”后面的那个部分。

2)  AttributeCultureAttributeVersion不能使用以上方法获取,只能使用AttributeName方法。因为它们是编译器生成的,不是直接输出供外部客户通过“右键”=>“属性”的方式直接可以看到的。

转载于:https://www.cnblogs.com/ServiceboyNew/archive/2011/11/17/2241215.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/261159.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《将博客搬至CSDN》

《将博客搬至CSDN》链接为&#xff1a;http://blog.csdn.net/scenlyf/转载于:https://blog.51cto.com/luoyafei/1787233

12306订票助手更新

由于时间关系&#xff0c;以及做了较大变更&#xff0c;订票助手已经很久发布更新了。但是订票助手我还是会一直维护下去&#xff0c;直到……你懂的。 这个版本比以前有较大变化&#xff0c;还存在许多已知和未知的问题&#xff0c;只建议喜欢尝鲜的朋友使用&#xff1a; 不再…

各种Exit退出函数用法

在VBScript中经常会遇到在执行到某个代码块时需要直接跳出的过程。 其实在Vbs语法中常用的有4种退出方式&#xff1a;Exit For、Exit Do、Exit Function、Exit Sub 1、Exit For 主要是用于for循环中 for i1 to 100 if i4 then msgbox "exit for" Exit For End If Ne…

oracle一个表拆成多个表,oracle – 在oracle表中将多个以逗号分隔的值拆分为多行...

接受的答案使用条件DBMS_RANDOM.VALUE IS NOT NULL这是不合适的。 它只是防止循环&#xff0c;但是一个简单的问题会出现&#xff0c; 如何和何时dbms_random.VALUE可以为空&#xff1f; 从逻辑上讲&#xff0c;它永远不会是NULL 。更合适的解决scheme是使用sys.odciNumberList…

python 正则表达式过滤文本中的html标签 源代码解析

#py2.7 #coding:utf-8import re import os import chardetdef filter_tag(htmlstr):re_cdata re.compile(<!DOCTYPE HTML PUBLIC[^>]*>, re.I)re_script re.compile(<\s*script[^>]*>[^<]*<\s*/\s*script\s*>, re.I) #过滤脚本re_style re.compi…

C++--Qt使用Http协议

2019独角兽企业重金招聘Python工程师标准>>> #include <QNetworkAccessManager>//包含QNetworkAccessManager类 #include <QNetworkRequest>//包含QNetworkRequest类 #include <QNetworkReply>//包含QNetworkReply类 #include <QtCore> #in…

Oracle数据库adg数据没同步,Oracle 11g备库无法开启ADG的原因分析

今天碰到一个有些奇怪的问题&#xff0c;但是奇怪的现象背后都是有本质的因果。下午在做一个环境的检查时&#xff0c;发现备库是在mount阶段&#xff0c;这可是一个11gR2的库&#xff0c;没有ADG实在是太浪费了&#xff0c;对于这种情况感觉太不应该了。所以尝试启动至open阶段…

linux基本安全配置手册

安装注意 作为服务器&#xff0c;不安装不需要的组件&#xff0c;所以在选择组件的时候&#xff0c;不要安装服务包和桌面但需要开发工具和开发包。 以下命令等适用redhat/centos 4,5 1.删除系统特殊的的用户帐号&#xff1a; 禁止所有默认的被操作系统本身启动的且不需要的帐号…

张邱建算经 卷下

周 甄 鸾 注经   唐 李淳风 注释   刘孝孙 撰细草   今有甲乙丙丁戊五人共猎获鹿约以甲六乙五丙四丁三戊二分之今获鹿五 问各得几何   答曰   甲得一鹿四分鹿之二   乙得一鹿四分鹿之一   丙得一鹿   丁得四分鹿之三   戊得四分鹿之二   术曰列置甲…

【转】使用JDK自带jvisualvm监控tomcat

转载地址: http://my.oschina.net/kone/blog/157239 jdk自带有个jvisualvm工具、该工具是用来监控java运行程序的cpu、内存、线程等的使用情况。并且使用图表的方式监控java程序、还具有远程监控能力。不失为一个用来监控tomcat的好工具。 在jdk目录下的bin目录中可以找到jvisu…

双网卡oracle rac,Linux+Oracle 10g RAC双网卡绑定和解除绑定的实现(2)

解除双网卡绑定十一、停数据库和crs在rac1和rac2上停数据库和crs#cd /u01/app/oracle/product/10.2.0/crs_1/bin停数据库实例#./srvctl stop instance –d devdb –i ora.devdb.devd1.inst#./srvctl stop instance –d devdb –i ora.devdb.devd2.inst停数据库#./srvctl stop …

推荐十款非常优秀的 HTML5 在线设计工具

网络有很多优秀的设计和开发工具可能大家都不知道&#xff0c;因此这篇文章就向设计师推荐十款优秀 HTML5 在线工具&#xff0c;这些工具能够帮助设计师们设计出更有创意的作品。随着 HTML5 技术的不断成熟&#xff0c;网络上涌现出越来越多的优秀 HTML5 应用&#xff0c;一起看…

【转】使用Apache Kylin搭建企业级开源大数据分析平台

http://www.thebigdata.cn/JieJueFangAn/30143.html 本篇文章整理自史少锋4月23日在『1024大数据技术峰会』上的分享实录&#xff1a;使用Apache Kylin搭建企业级开源大数据分析平台。 正文如下 我先做一个简单介绍我叫史少锋&#xff0c;我曾经在IBM、eBay做过大数据、云架构的…

每日英语:Apple Unveils New iPads

Apple Inc. s answer to the increasingly cutthroat tablet-computer market: more product choices and free software. cutthroat&#xff1a;残酷的&#xff0c;剧烈的&#xff0c;凶手    At an event here Tuesday, Apple showed off a new full-size tablet--now ca…

wordpress漏洞上传php文件夹,WordPress Asset-Manager PHP文件上传漏洞

这个模块利用Metasploi脆弱漏洞库在WordPress版本Asset-Manager插件2.0以及以下版本发现的。允许上传php文件、一用户可以上传一个文件到一个临时目录没有身份验证,从而导致执行任意代码。### This file is part of the Metasploit Framework and may be subject to# redistrib…

C#基于Socket的CS模式的完整例子

基于Socket服务器端实现本例主要是建立多客户端与服务器之间的数据传输&#xff0c;首先设计服务器。打开VS2008&#xff0c;在D:\C#\ch17目录下建立名为SocketServer的Windows应用程序。打开工程&#xff0c;往当前窗体中添加控件&#xff0c;如表17-6所示。表17-6 添加控件列…

Java基础之写文件——缓冲区中的多条记录(PrimesToFile3)

控制台程序&#xff0c;上一条博文&#xff08;PrimesToFile2&#xff09;每次将一个素数写入到文件中&#xff0c;所以效率不是很高。最好是使用更大的缓冲区并加载多个素数。 本例重复使用三个不同的视图缓冲区加载字节缓冲区并尽可能加入更多的素数&#xff0c;推荐使用这种…

导出oracle sequences,利用数据泵只导出序列

Oracle的数据泵导入导出功能比原有的导入导出工具(exp/imp)功能强很多。利用数据泵我们可以只导出某一特定对象类型&#xff0c;并且可以指定过滤条件。这个功能的实现主要依靠expdp的include参数。联机文档对于参数的功能描述如下&#xff1a;INCLUDEDefault: nonePurposeEnab…

HDU2546_用01背包做

题目大意&#xff1a; 电子科大本部食堂的饭卡有一种很诡异的设计&#xff0c;即在购买之前判断余额。如果购买一个商品之前&#xff0c;卡上的剩余金额大于或等于5元&#xff0c;就一定可以购买成功&#xff08;即使购买后卡上余额为负&#xff09;&#xff0c;否则无…

俄罗斯游戏软件:C语言应用初步感受

俄罗斯游戏软件&#xff1a;C语言应用初步感受C语言课程设以一节课&#xff0c;老师提供了一个C语言的飞俄罗斯方块让我们感受&#xff0c;我们所学的C语言课程&#xff0c;主要是各种语句的练习&#xff0c;这次是用我们所学过的知识来感受一个实际的系统。首先安装c-free&…