面试例题8:如何动态加载外部程序集并用反射获取指定类型的信息?
考点:学习动态加载外部程序集的基本方法,理解Assembly类的Load()方法和LoadFrom()方法的区别。
出现频率:★★★
解答
通过System.Reflection命名空间下的Assembly类可以动态加载外部程序集,可选方法为Load()方法和 LoadFrom()方法。Load()方法用于加载当前程序集位于相同目录下的外部程序集,LoadFrom()方法可以加载其他目录中的程序集。以上示例中的ClassRef.exe程序作为需要加载的外部程序集,进一步获取其指定类型的信息。在目录下新建一个程序文件,并命名为 AppClassRef.cs,编写代码如代码7.8所示。
代码7.8 动态加载外部程序集:
//导入相应的命名空间
using System.Reflection;
using System.IO;
class AppClassRef
{
static void Main(string[] args)
{
//查看当前应用程序域的所有程序集
DisAllAm();
while (true)
{
Console.Write("请输入所检测的类型名称:");
//接收用户输入值并赋值给input变量
string input = Console.ReadLine();
//如果用户输入"quit",则跳出循环
if (input == "quit")
{
break;
}
//声明Assembly类型对象am
Assembly am;
try
{
//调用Assembly类的Load方法,将引用传递给am
am = Assembly.Load("ClassRef");
//以下代码调用Assembly类的LoadFrom方法
//am = Assembly.LoadFrom(@"D:/web/NET/ClassRef.exe");
//调用am的GetType方法,并将Type对象引用返回给tp变量
Type tp = am.GetType(input, false, false);
//调用ClassB的Ref静态方法,并传递tp对象
ClassB.Ref(tp);
}
//捕获文件未找到异常
catch (FileNotFoundException e)
{
//输出异常信息
Console.WriteLine("异常信息:{0}", e.Message);
}
//捕获空对象引用异常
catch (NullReferenceException e)
{
//输出异常信息
Console.WriteLine("异常信息:{0}", e.Message);
}
//捕获一般异常
catch (Exception e)
{
//输出异常信息
Console.WriteLine("异常信息:{0}", e.Message);
}
finally
{
//再次查看当前应用程序域的所有程序集
DisAllAm();
}
}
}
//定义DisAllAm方法,用于显示程序集列表
static void DisAllAm()
{
Console.WriteLine("/n/t=============当前应用程序域中所有加载的程序集=============");
//通过GetAssemblies方法获取当前应用程序域的所有程序集
foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
{
//输出程序集的名称
Console.WriteLine("程序集:{0}", a.FullName);
}
}
}
class ClassB
{
//定义静态方法Ref,接收一个Type类型的参数
public static void Ref(Type tp)
{
//输出Type对象的基本属性
string FullName = tp.FullName.ToString();
Console.WriteLine("/n/t============={0}类型的信息=============", FullName);
//获取Type对象的所有公共成员并保存到mi数组
MemberInfo[] mi = tp.GetMembers();
//遍历并输出mi数组所有的子项属性
foreach (MemberInfo m in mi)
{
Console.WriteLine("/t成员类别->{0},名称->{1}",m.MemberType, m.Name);
}
//获取Type对象所实现的接口并保存到Itp数组
Type[] Itp = tp.GetInterfaces();
//判断Itp数组是否有子项,如果有则输出子项属性
if (Itp.Length != 0)
{
foreach (Type t in Itp)
{
Console.WriteLine("{0}实现的接口类型->{1}", FullName, t.FullName);
}
}
else
{
Console.WriteLine("{0}不实现的任何接口类型", FullName);
}
}
}
本题的类型信息输出和ClassRef.exe相同,这里不作过多讲述。主程序中首先调用了DisAllAm()静态方法,该方法封装了列举当前应用程序域所含程序集列表的代码。在命令行下编译AppClassRef.cs执行AppClassRef程序,运行结果如图7.13所示。
本程序首先输出了当前应用程序域所包含程序集的列表,可知当前仅有mscorlib程序集和AppClassRef程序集被加载。继续操作,再次调用DisAllAm()方法,所输出的程序集列表中已经加入了ClassRef程序集。
图7.13 动态加载外部程序集
注意:mscorlib程序集包含了.NET大部分的基类库,CLR运行时自动加载。
解析
程序动态加载外部程序集,通过System.Reflection命名空间下的Assembly类实现,该类的Load()方法和 LoadFrom()方法可以在运行时加载指定的外部程序集。加载外部程序集后,Assembly类将创建相应的对象,调用该对象的GetType()方法可获取指定类型的详细信息。假设加载D:/根目录下的MyApp.exe程序集,并且定义Assembly类的对象am,其如以下代码所示:
序直接输出了ClassA类的细节信息。再次输入"ClassB",程序输出ClassB类的细节信息,运行结果如图7.10所示。
//导入相应的命名空间
using System.Reflection;
using System.IO;
Assembly am;
try
{
am = Assembly.Load("MyApp");
Type tp = am.GetType("指定类型名称",false,false);
}
catch (FileNotFoundException e)
{
Console.WriteLine("异常信息:{0}", e.Message);
}
以上代码调用了Assembly类的Load()方法载入MyApp程序集(exe或dll),代码正常加载的前提是当前程序集和MyApp程序集位于相同目录。所以,Assembly类的LoadFrom()方法也比较常用,其编写方法如下所示:
可见,Assembly类的LoadFrom()方法需要传递外部程序集的完整路径。当外部程序集加载后,可以通过访问当前应用程序域的所含程序集,以确定加载是否成功。需要注意System.IO命名空间的导入,因为主程序中需要捕捉"文件未找到"的异常,防止外部程序集不存在。
说明:读取AppDomain类的CurrentDomain属性,可返回当前应用程序域,调用当前应用程序域的GetAssemblies方法,可获取所包含程序集的集合。
面试例题9:如何通过晚期绑定调用方法成员?
考点:晚期绑定的含义,晚期绑定的基本方法。
出现频率:★★★
解答
晚期绑定即在编译时不能确定是否存在类型,在运行时创建该类型的实例,调用其成员。本例程序分别定义了3个外部类型,编译为dll程序集,由 LateBinding.cs代码动态加载,调用其内部方法。在目录下新建一个程序文件,并命名为OldClass.cs,编写代码如代码7.9所示。
代码7.9 第1个外部类:OldClass.cs
{
//定义public权限的Method静态方法
public static string Method(string s)
{
string output = "OldClass类的Method静态方法返回结果:" + s;
return output;
}
}
在目录下新建一个程序文件,并命名为NewClass.cs,编写代码如代码7.10所示。
代码7.10 第2个外部类:NewClass.cs
{
//定义public权限的Method实例方法
public string Method(string s)
{
string output = "NewClass类的Method实例方法返回结果:" + s;
return output;
}
}
在目录下新建一个程序文件,并命名为MyClass.cs,编写代码如代码7.11所示。
代码7.11 第3个外部类:MyClass.cs
{
//定义public权限的Method实例方法
public string Method(string s)
{
string output = "MyClass类的Method实例方法返回结果:" + s;
return output;
}
//定义public权限的MethodTxt实例方法
public string MethodTxt()
{
string output = "MyClass类的MethodTxt无参数实例方法被调用";
return output;
}
}