C#基础之Assembly

  一直以来,我们都在用C#编写程序,编写程序的时候,我们用到继承、多态、接口以及泛型,我们也都明白子类可以继承抽象类,并能够重写父类的抽象方法,可是大家是否想过,如下几个问题:

  1、凡树必有根和叶,类的继承也如此,如何通过程序集查找所有继承父类的之类的程序集名称?

  2、如果程序B被其他程序调用,如何通过程序集查询调用B的所有程序?

  3、如何查询当前项目通过添加引用了哪些程序集?

一、查询

查询继承父类的程序集合BaseType

  .NET的程序集对象Assembly有个属性BaseType,来返回当前程序集的基础类型,默认是Object。通过该属性可以判断某个程序集是否是继承了某个父类;

程序集B被A程序引用,获取A程序集的信息GetCallingAssembly

  .NET的程序集对象Assembly有个方法GetCallingAssembly获取当前程序集被调用的上级程序集的信息;

查找当前项目的程序集GetAssemblies

  查找当前项目所有程序集的方法System.AppDomain.CurrentDomain.GetAssemblies();

实例化对象

  通过程序集GetType()获取程序集的数据类型,通过Activator.CreateInstance(type) 便可创建响应的实例对象;

     整体使用的代码如下:BaseService是所有服务类的基类

List<Type> TypeItemList = new List<Type>();
            //var ResultTypeList = Assembly.GetEntryAssembly();
            //if (ResultTypeList == null)
            //{
            //    ResultTypeList = Assembly.GetCallingAssembly();
            //    var ItemList = ResultTypeList.GetReferencedAssemblies().Where(p => p.GetType() == typeof(BaseService));                
            //}
            Assembly[] AssbyCustmList = System.AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly assItem in AssbyCustmList)
            {
                TypeItemList.AddRange(assItem.GetTypes().Where(x => x.BaseType == typeof(BaseService)).ToList());
            }
            //IEnumerable<Type> TypeItemList = CurType.Assembly.GetExportedTypes().Where(x => x.BaseType == typeof(BaseService)).ToList();
            //IEnumerable<Type> TypeItemList = ResultTypeList.GetTypes().Where(x => x.BaseType == typeof(BaseService)).ToList();
            BaseService TarService = null;
            foreach (Type typeitem in TypeItemList)
            {
                if (_Reporttory.Count(p => p.GetType() == typeitem) == 0)
                {
                    TarService = (Activator.CreateInstance(typeitem) as BaseService);
                }
                else
                {
                    TarService = _Reporttory.First(p => p.GetType() == typeitem);
                }
                TarService.Start();
            }         

测试代码

 static void ShowAssemblyDetail(Assembly assembly, string name = "")
        {
            Console.WriteLine("============ \n");
            Console.WriteLine("FullName:" + assembly.FullName);
            Console.WriteLine("CodeBase:" + assembly.CodeBase);
            Console.WriteLine("EscapedCodeBase:" + assembly.EscapedCodeBase);
            Console.WriteLine("GlobalAssemblyCache:" + assembly.GlobalAssemblyCache);
            Console.WriteLine("HostContext:" + assembly.HostContext);
            Console.WriteLine("ImageRuntimeVersion:" + assembly.ImageRuntimeVersion);
            Console.WriteLine("IsDynamic:" + assembly.IsDynamic);
            Console.WriteLine("IsFullyTrusted:" + assembly.IsFullyTrusted);
            Console.WriteLine("Location:" + assembly.Location);
            Console.WriteLine("ReflectionOnly:" + assembly.ReflectionOnly);
            Console.WriteLine("SecurityRuleSet:" + assembly.SecurityRuleSet);
        }

 二、加载方法

关于.NET中的反射,常用的有三个方法:
Assembly.Load()
Assembly.LoadFrom()
Assembly.LoadFile()

1:Assembly.Load()

  Load()方法接收一个String或AssemblyName类型作为参数,这个参数实际上是需要加载的程序集的强名称(程序集名,版本,语言文化,公钥标记)。
它会加载此程序集引用的其他程序集,一般情况下都应该优先使用 这个方法,他的执行效率比LoadFrom要高很多,而且不会造成重复加载的问题(原因在第2点上说明)。
使用这个方法的时候, CLR会应用一定的策略来查找程序集,实际上CLR按如下的顺序来定位程序集:
  ⑴如果程序集有强名称,在首先在全局程序集缓(GAC)中查找程序集。
  ⑵如果程序集的强名称没有正确指定或GAC中找不到,那么通过配置文件中的<codebase>元素指定的URL来查找
  ⑶如果没有指定强名称或是在GAC中找不到,CLR会探测特定的文件夹:
假设你的应用程序目录是C:\AppDir,<probing>元素中的privatePath指定了一个路径Path1,你要定位的程序集是AssemblyName.dll,
则CLR将按照如下顺序定位程序集
  C:\AppDir\AssemblyName.dll
  C:\AppDir\AssemblyName\AssemblyName.dll
  C:\AppDir\Path1\AssemblyName.dll
  C:\AppDir\Path1\AssemblyName\AssemblyName.dll

如果以上方法不能找到程序集,会发生编译错误,如果是动态加载程序集,会在运行时抛出异常!

2:Assembly.LoadFrom()

  这个方法从指定的路径来加载程序集,实际上这个方法被调用的时候,CLR会打开这个文件,通过查找程序集的AssemblyRef元数据表,得知所有引用和需要的程序集,获取其中的程序集版本,语言文化,公钥标记等信息,把他
们传递给 Load方法,接着,Load方法采用上面的策略来查找程序集。
  如果找到了程序集,会和LoadFrom方法中指定的路径做比较,如果路径相同,该程序集会被认为是应用程序的一部分;
  如果路径不同或Load方法没有找到程序集,那该程序集只是被作为一个“数据文件”来加载,不会被认为是应用程序的一部分。
  这就是在第1点中提到的Load方法比LoadFrom方法的执行效率高的原因。
另外,由于可能把程序集作为“数据文件”来加载,所以使用 LoadFrom从不同路径加载相同程序集的时候会导致重复加载。当然这个方法会加载此程序集引用的其他程序集。
Assembly.LoadFrom(@"C:\ABC\Test.dll");

3:Assembly.LoadFile()

  这个方法是从指定的文件来加载程序集,和上面方法的不同之处是这个方法不会加载此程序集引用的其他程序集,需要自己控制并显示加载所有依赖的程序集!


结论:一般大家应该优先选择Load方法来加载程序集,如果遇到需要使用LoadFrom方法的时候,最好改变设计而用Load方法来代替!

LoadFile与LoadFrom的区别

  1、Assembly.LoadFile只载入相应的dll文件,比如Assembly.LoadFile("abc.dll"),则载入abc.dll,假如abc.dll中引用了def.dll的话,def.dll并不会被载入。
Assembly.LoadFrom则不一样,它会载入dll文件及其引用的其他dll,比如上面的例子,def.dll也会被载入。

  2、用Assembly.LoadFrom载入一个Assembly时,会先检查前面是否已经载入过相同名字的Assembly,比如abc.dll有两个版本(版本1在目录1下,版本2放在目录2下),程序一开始时载入了版本1,当使用Assembly.LoadFrom("2\\abc.dll")载入版本2时,不能载入,而是返回版本1。
  Assembly.LoadFile的话则不会做这样的检查,比如上面的例子换成Assembly.LoadFile的话,则能正确载入版本2。
  LoadFile:加载指定路径上的程序集文件的内容。LoadFrom: 根据程序集的文件名加载程序集文件的内容。

区别:

  LoadFile 方法用来来加载和检查具有相同标识但位于不同路径中的程序集.但不会加载程序的依赖项。
  LoadFrom 不能用于加载标识相同但路径不同的程序集。

三、示例

 

 

 

 

参考文档:

https://www.cnblogs.com/xibei666/p/6243424.html

https://www.cnblogs.com/SavionZhang/p/6531234.html

http://blog.sina.com.cn/s/blog_7e60af8b01015mfu.html (推荐)

https://blog.csdn.net/aaa123524457/article/details/53242877

https://www.cnblogs.com/danielWise/archive/2011/09/07/2170042.html

 

posted @ 2018-05-17 17:56  心存善念  阅读(32184)  评论(0编辑  收藏  举报