[转] 接触C# 反射 2

时间:2022-01-18 11:40:55

逆心 原文 反射基础,2013-4.

反射用于在程序运行过程中,获取类里面的信息或发现程序集并运行的一个过程。通过反射可以获得.dll和.exe后缀的程序集里面的信息。使用反射可以看到一个程序集内部的类,接口,字段,属性,方法,特性等信息。

一、各种GetType()、typeof的区别

  首先就是获取Tyoe对象的来源不同:

    class Program
{
static void Main(string[] args)
{ Type t1 = Type.GetType("ConsoleApplication2.Person"); //从字符串中获得Type对象
Console.WriteLine(t1.ToString()); Type t2 = typeof(ConsoleApplication2.Person); //从具体类中获得Type对象
Console.WriteLine(t2.ToString()); Person p = new Person();
Type t3 = p.GetType(); //实例,从实例中获得Type对象 Assembly ass = Assembly.LoadFrom(@"C:\Users\Administrator\Desktop\ConsoleApplication2\ConsoleApplication2\bin\Debug\ConsoleApplication2.exe");
Console.WriteLine(ass.GetType("ConsoleApplication2.Person").ToString()); //从字符串中获得Type对象
Module mod = ass.GetModules()[0];
Console.WriteLine(mod.GetType("ConsoleApplication2.Person").ToString()); //从字符串中获得Type对象 Console.ReadKey();
}
}

  三者的区别在于typeof()和Type.GetType()是从一个类中获取对象,而Object.GetType()是从一个类的实例获得对象。

  而前两者的区别在于:

  • 只有typeof()是运算符。
  • Type.GetType()是实例方法。
  • Object.GetType()是基类System.Object的方法(无参数,在Type类、Assembly类、Module类中都有这个无参方法),实例方法。
  • Assembly.GetType() 、Module.GetType()、Type.Get()都是各自对象的实例方法。

在System.Reflection命名空间内包含多个反射常用的类,下面表格列出了常用的几个类。

类型 作用
Assembly 通过此类可以加载操纵一个程序集,并获取程序集内部信息
EventInfo 该类保存给定的事件信息
FieldInfo 该类保存给定的字段信息
MethodInfo 该类保存给定的方法信息
MemberInfo 该类是一个基类,它定义了EventInfo、FieldInfo、MethodInfo、PropertyInfo的多个公用行为
Module 该类可以使你能访问多个程序集中的给定模块
ParameterInfo 该类保存给定的参数信息
PropertyInfo 该类保存给定的属性信息

二、System.Reflection.Assembly类 

通过Assembly可以动态加载程序集,并查看程序集的内部信息,其中最常用的就是Load()这个方法。

Assembly assembly = Assembly.Load("MyAssembly");  

  注意在Assembly里面的加载程序集有3个方法,分别是Load、LoadFrom和LoadFile。这3个方法有什么异同呢?

  1、如果你引用了命名空间,那么就直接Load()方法,参数里面写上命名空间+类名就可以加载了。

  2、如果仅仅知道一个dll文件的那么就要用LoadFrom()方法了,参数里面直接填写完整的路径。

LoadFrom 方法具有以下缺点。请考虑改用 Load。

-如果已加载一个具有相同标识的程序集,则即使指定了不同的路径,LoadFrom 仍返回已加载的程序集。 
-如果用 LoadFrom 加载一个程序集,随后加载上下文中的一个程序集尝试加载具有相同显示名称的程序集,则加载尝试将失败。对程序集进行反序列化时,可能发生这种情况。

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

3、LoadFile (加载指定路径上的程序集文件的内容。)

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

  利用Assembly的object CreateInstance(string)方法可以反射创建一个对象,参数0为类名。

    class Program
{
static void Main(string[] args)
{
Assembly assm = Assembly.Load("fanshe");
Console.WriteLine(assm.FullName); //输出 fanshe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null //注释上面两行,移除程序集的引用 Assembly assm1 = Assembly.LoadFrom(@"D:\fanshe.dll");
Console.WriteLine(assm1.FullName); ////输出 fanshe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null //与Assembly.LoadFrom基本一样,只是如果被加载的dll,还依赖其他的dll的话,被依赖的对象不会加载
Assembly assm2 = Assembly.LoadFile(@"D:\fanshe.dll");
Console.WriteLine(assm2.FullName); Console.ReadKey();
}
}

三、System.Type类

Type是最常用到的类,它一般用于装载反射得到的类对象,通过Type可以得到一个类的内部信息,也可以通过它反射创建一个对象。一般有三个常用的方法可以得到Type对象。

1.利用typeof()得到Type对象

  Type type = typeof(Example);

2.利用System.Object.GetType()得到Type对象

  Example example = new Example();
  Type type = example.GetType();

3.利用System.Type.GetType()得到Type对象

Type type = Type.GetType("MyAssembly.Example",false,true)   //注意0是类名,参数1表示若找不到对应类时是否抛出异常,参数2表示类名是否区分大小写

示例:

    public class Program
{
static void Main(string[] args)
{
Person p1 = new Person(); Type t1 = typeof(Person);
Type t2 = p1.GetType(); Person p2 = Activator.CreateInstance(t1) as Person;
Person p3 = Activator.CreateInstance(t2) as Person; Console.ReadKey();
}
} public class Person
{
public int Id { get; set; } public string Name { get; set; }
}

四、反射方法

1.通过 Type.GetMethods()能查找到类里面的方法

代码示例:

    class Program
{
static void Main(string[] args)
{
Type t = typeof(Person);
MethodInfo[] MethodInfoList = t.GetMethods();
foreach (MethodInfo info in MethodInfoList)
{
Console.WriteLine(info);
} Console.ReadKey();
}
}

  输出结果如下:

    [转] 接触C# 反射 2

  留意到里面所有的方法,包括继承来的都列出来了。另外可以留意到,属性的读取设置,在根本上也是一个方法。

  调用反射得到的方法使用Invoke方法(),示例如下:

        static void Main(string[] args)
{
Assembly assembly = Assembly.Load("fanshe");
Type type = assembly.GetType("fanshe.Person"); //注意要输入全部路径,包括命名空间
object obj = Activator.CreateInstance(type);
MethodInfo methodShow = type.GetMethod("Show"); //根据方法名获取MethodInfo对象
//调用无参方法Show,Invoke表示执行方法
methodShow.Invoke(obj, null); // 参数1类型为object[],代表Hello World方法的对应参数,输入值为null代表没有参数 Console.ReadKey();
}

五、反射属性

  1、通过System.Reflection.Property能查找到类里面的属性
  常用的方法有GetValue(object,object[])获取属性值和SetValue(object,object,object[])设置属性值

  代码示例:

    class Program
{
static void Main(string[] args)
{
Type t = typeof(Person);
PropertyInfo[] PropertyInfoList = t.GetProperties();
foreach (PropertyInfo info in PropertyInfoList)
{
Console.WriteLine(info);
} Console.ReadKey();
}
}

  输出结果如下:

    [转] 接触C# 反射 2

  2、另外还可以通过PropertyInfo对象的GetValue和SetValue方法读取和设置创建出来的对象的属性值

    class Program
{
static void Main(string[] args)
{
Assembly assembly = Assembly.Load("fanshe");
Type type = assembly.GetType("fanshe.Person"); //注意要输入全部路径,包括命名空间
object obj = Activator.CreateInstance(type); //调用要属性的方法
PropertyInfo propertyName = type.GetProperty("Name"); //获取Name属性对象
propertyName.SetValue(obj, "张飞", null); //设置Name属性的值 object objName = propertyName.GetValue(obj, null); //获取属性值
Console.WriteLine(objName); //输出张飞 Console.ReadKey();
}
}  

  下面给出一个方法与属性的综合示例:

class Program
{
static void Main(string[] args)
{
Assembly assembly = Assembly.Load("fanshe");
Type type = assembly.GetType("fanshe.Person"); //注意要输入全部路径,包括命名空间
object obj = Activator.CreateInstance(type);
MethodInfo methodShow = type.GetMethod("Show"); //根据方法名获取MethodInfo对象
//调用无参方法Show,Invoke表示执行方法
methodShow.Invoke(obj, null); // 参数1类型为object[],代表Hello World方法的对应参数,输入值为null代表没有参数 //调用带参数方法
MethodInfo methodAdd = type.GetMethod("Add");
object[] objArr = new object[]{1,2};
methodAdd.Invoke(obj,objArr); //输出3 第二个参数为传入去的参数列表 //调用要属性的方法
PropertyInfo propertyName = type.GetProperty("Name"); //获取Name属性对象
propertyName.SetValue(obj,"张飞",null); //设置Name属性的值
PropertyInfo propertyAge = type.GetProperty("Age"); //获取Age属性对象
propertyAge.SetValue(obj, 24, null); //把Age属性设置为34
MethodInfo methodSay = type.GetMethod("Say");
methodSay.Invoke(obj,null); //输出我的名字叫张飞,我今年24岁
Console.ReadKey();
}
}

  Person类的代码如下:

namespace fanshe
{
public class Person
{
private string name;
public string Name get { return name; } set { name = value; } } private int age;
public int Age { get { return age; } set { age = value; } } public void Show() { Console.WriteLine("我是Person类里的Show方法!"); } public void Say() { Console.WriteLine("我的名字叫{0},我今年{1}岁", this.Name, this.Age); } public void Add(int i, int j) { Console.WriteLine(i + j); }
}
}

  2、根据属性的类型设置属性的值:

  我们有可能会一次过设置很多属性的值,而这些属性里面可能有字符串类型、整型等等。因此,我们需要动态设置,这是需要根据属性的类型设置属性的值。

namespace DynamicSetValue
{
class Program
{
static void Main(string[] args)
{
Type type = typeof(Person); //注意要输入全部路径,包括命名空间
object obj = Activator.CreateInstance(type);
//假设这是存在于XML的数据
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.Add("Id", "1");
dic.Add("Name", "神灵武士");
dic.Add("Birthday", "2001-01-01"); PropertyInfo[] ProArr = type.GetProperties();
foreach (PropertyInfo p in ProArr)
{
if (dic.Keys.Contains(p.Name))
{
p.SetValue(obj, Convert.ChangeType(dic[p.Name], p.PropertyType), null);  //当需要给属性设置不同类型的值时
}
} Person person = obj as Person;
Console.WriteLine(person.Birthday); Console.ReadKey();
}
} //有三种不同类型的属性
public class Person
{
public int Id { get; set; }
public string Name { get; set;}
public DateTime Birthday { get; set; }
}
}

  输出如下:

  [转] 接触C# 反射 2

  如果不根据类型来转换,则报如下错误:

  [转] 接触C# 反射 2

六、反射字段

  通过 System.Reflection.FieldInfo 能查找到类里面的字段

  它包括有两个常用方法SetValue(object ,object )和GetValue(object)  因为使用方法与反射属性非常相似。

    class Program
{
static void Main(string[] args)
{
Assembly assembly = Assembly.Load("fanshe");
Type type = assembly.GetType("fanshe.Person"); //注意要输入全部路径,包括命名空间
object obj = Activator.CreateInstance(type); //调用要属性的方法
FieldInfo fieldName = type.GetField("name", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance); //获取name字段对象,后面的那个枚举参数指定,非公开字段也搜索,即读取private的name字段
fieldName.SetValue(obj, "张飞"); //设置name字段的值 object objName = fieldName.GetValue(obj); //获取属性值
Console.WriteLine(objName); //输出张飞 Console.ReadKey();
}
}

七、反射特性

  通过System.Reflection.MemberInfo的GetCustomAttributes(Type,bool)就可反射出一个类里面的特性。

    class Program
{
static void Main(string[] args)
{
Assembly assembly = Assembly.Load("fanshe");
Type type = assembly.GetType("fanshe.Person"); //注意要输入全部路径,包括命名空间
object obj = Activator.CreateInstance(type);
object[] typeAttributes=type.GetCustomAttributes(false); //获取Person类的特性
foreach (object attribute in typeAttributes)
{
Console.WriteLine(attribute.ToString()); //输出 System.SerializableAttribute 因为我在Person上里加了个[Serializable]
} Console.ReadKey();
}
}

八、应用实例

  利用反射实现的简单工厂模式的多数据库系统实例:

  App.Config配置文件

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="DAL" value="ConsoleApplication2.Access"/>
</appSettings>
</configuration>

  主程序代码:

namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Assembly assembly = Assembly.Load("ConsoleApplication2");
string str = System.Configuration.ConfigurationManager.AppSettings["DAL"];
Type type = assembly.GetType(str); //注意真正实现的对象路径写在了外面,这样要更改数据库只要改配置文件,不需要改动程序!
IDAL DAL = (IDAL)Activator.CreateInstance(type);
DAL.Insert(); Console.ReadKey();
}
} interface IDAL
{
void Insert();
} class SqlServer : IDAL
{
public void Insert()
{
Console.WriteLine("SqlServer增加一条记录!");
}
} class Access : IDAL
{
public void Insert()
{
Console.WriteLine("Access增加一条记录!");
}
}
}

  2、不引用命名空间根据路径运行dll里的方法示例

  Person类代码:

namespace fanshe
{
public class Person
{
private string name;
public string Name
{ get { return name; } { name = value; } } private int age;
public int Age
{ get { return age; } set { age = value; } } public string Say(string str)
{
Console.WriteLine("我的名字叫{0},我今年{1}岁,另外你给我传入的参数是:{2}", this.Name, this.Age,str);
return "返回结果";
}
}
}

  主程序代码:

    class Program
{
static void Main(string[] args)
{
//先把引用的命名空间移除
Assembly assembly = Assembly.LoadFrom(@"D:\fanshe.dll");
Type type = assembly.GetType("fanshe.Person");
object obj = Activator.CreateInstance(type);
PropertyInfo proName = type.GetProperty("Name");
proName.SetValue(obj,"关羽",null);
PropertyInfo proAge = type.GetProperty("Age");
proAge.SetValue(obj,24,null);
MethodInfo methodSay = type.GetMethod("Say");
object[] objList = new object[1]{"传入测试参数"};
object result = methodSay.Invoke(obj,objList);
Console.WriteLine(result);  //输出 返回值 Console.ReadKey();
}
}

  3、获得List<T>中的T类型:

List<Dog> dogs = new List<Dog>();
Type type = dogs.GetType();
if (type.IsGenericType)
{
  Type[] genericArgTypes = type.GetGenericArguments();
  if (genericArgTypes[0] == typeof(Dog))
  {
  //你想要判断的是这个吗?
  }
}

  当然,如果List<T>是你定义的泛型,那么直接typeof(T)更加简单。

[转] 接触C# 反射 2的更多相关文章

  1. 接触C&num; 反射

    1.反射的概念详解[1] 1.1 理解C#中的反射 1.B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内脏的生理情况.这是如何做到的呢?B超是B型超声波,它可以透过肚皮通过向你体内 发 ...

  2. &period;Net 中的反射&lpar;序章&rpar; - Part&period;1

    引言 反射是.Net提供给我们的一件强力武器,尽管大多数情况下我们不常用到反射,尽管我们可能也不需要精通它,但对反射的使用作以初步了解在日后的开发中或许会有所帮助. 反射是一个庞大的话题,牵扯到的知识 ...

  3. C&num;反射&lpar;二&rpar; 【转】

    如果没有看<C#反射(一)>.建议先看<C#反射(一)>再看这一篇.上一篇文章发表,有人评论我所写的东西比较基础.其实我也知道我也只不过是在写最基础的语法而已,之所以写它是因为 ...

  4. Java的反射机制及应用实例

    一:什么是反射机制 简单的来说,反射机制指的是程序在运行时能够获取自身的信息.在Java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息. 二:哪里用到反射机制 我们用过一些知识,但是并 ...

  5. DSAPI多功能组件编程应用-反射相关

    [DSAPI.DLL下载地址]       在.Net中,反射技术是一种入门困难,熟用快速的东西,对于没有接触过反射技术的程序员来说的确是头疼的,看一旦自己写过了,上手就非常简单了.在本节,将部分.N ...

  6. Java高新技术第二篇:反射技术

    今天我们来看一下Java中的反射技术: 首先来了解一下Java中的反射的一些概念: Java中的反射是1.2引入的 反射的基石:class类 Class类的各个实例对象分别对应各个类在内存中的字节码, ...

  7. PhysicalBasedRendering&lpar;一&rpar;物理篇

    很多人对PBR的理解是存在偏差的,跳不出传统渲染模型的思维圈子,把它理解成一种模拟效果更为精确的算法公式,虽然在某种程度上是对的,但没有看到PBR的本质. PBR是对光在真实世界中与环境交互的一种近似 ...

  8. C&num;提高-------------------Module的使用

    如果没有看<C#反射(一)>.建议先看<C#反射(一)>再看这一篇.上一篇文章发表,有人评论我所写的东西比较基础.其实我也知道我也只不过是在写最基础的语法而已,之所以写它是因为 ...

  9. web安全之跨站脚本漏洞(XSS)

    XSS(跨站脚本)概述以及pikachu上的实验操作 Cross-Site Scripting 简称为“CSS”,为避免与前端叠成样式表的缩写"CSS"冲突,故又称XSS. XSS ...

随机推荐

  1. 使用Autolayout对多行文本Label进行布局,高度不准确的解决办法!

    BUG描述: 今天公司的项目中发现了一个BUG,大概给大家描述一下,tabbleView有一个tableFooterView,这个footView中有一个Label,是多行显示文本,程序用的是Auto ...

  2. MySQL 分区表

    转载自MySQL 分区表 今天统计数据的时候发现一张表使用了表分区,借此机会记录一下. 1. 什么是表分区? 表分区,是指根据一定规则,将数据库中的一张表分解成多个更小的,容易管理的部分.从逻辑上看, ...

  3. Selenium2&plus;python自动化14-iframe

    前言 有很多小伙伴在拿163作为登录案例的时候,发现不管怎么定位都无法定位到,到底是什么鬼呢,本篇详细介绍iframe相关的切换 以http://mail.163.com/登录页面10为案例,详细介绍 ...

  4. 【MVC 4】7&period;SportsSore:完成购物车

    作者:[美]Adam Freeman      来源:<精通ASP.NET MVC 4> 本文将继续构建 SportsStore 示例应用程序.在上一章中,添加了对购物车的基本支持,现在打 ...

  5. Office Web Apps资源

    http://www.cnblogs.com/poissonnotes/p/3277280.html#!comments http://www.cnblogs.com/poissonnotes/p/3 ...

  6. C&plus;&plus;builder 图像字符流的存储和加载

    __fastcall TForm6::TForm6(TComponent* Owner) : TForm(Owner) { #if 1 //for debug AllocConsole(); Atta ...

  7. &period;net 中的DllImport

    只有做成COM的C++ dll才能直接引用.没有做成COM的就只能用P/Invoke(DllImport)或者C++/CLI那种.不过P/Invoke容易类型对不上,所以要是函数多,最好用C++/CL ...

  8. Python使用Tabula提取PDF表格数据

    今天遇到一个批量读取pdf文件中表格数据的需求,样式大体是以下这样: python读取PDF无非就是三种方式(我所了解的),pdfminer.pdf2htmlEX 和 Tabula.综合考虑后,选择了 ...

  9. 吴恩达机器学习笔记35-诊断偏差和方差&lpar;Diagnosing Bias vs&period; Variance&rpar;

    当你运行一个学习算法时,如果这个算法的表现不理想,那么多半是出现两种情况:要么是偏差比较大,要么是方差比较大.换句话说,出现的情况要么是欠拟合,要么是过拟合问题.那么这两种情况,哪个和偏差有关,哪个和 ...

  10. Confluence 6 创建一个空间

    在 Confluence 中并不限制你可以创建多少空间.你可以选择为你每一个小组,项目都创建一个空间,或者你也可以将这 2 者混合在一起.所有的这些都基于你的需求来决定的. 每一个 Confluenc ...