1、反射的概念详解[1]

1.1 理解C#中的反射

1、B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内脏的生理情况。这是如何做到的呢?B超是B型超声波,它可以透过肚皮通过向你体内 发射B型超声波,当超声波遇到内脏壁的时候就会产生一定的“回音”反射,然后把“回音”进行处理就可以显示出内脏的情况了(我不是医生也不是声学专家,不 知说得是否准确^_^)。

2、地球内部结构:地球的内部结构大体可以分为三层:地壳、地幔和地核。地壳是固体,地核是液体,地幔则是半液半固的结构(中学地理的内容,大家还记得
吧?)。如何在地球表面不用深入地球内部就知道其内部的构造呢?对,向地球发射“地震波”,“地震波”分两种一种是“横波”,另一种是“纵波”。“横波”
只能穿透固体,而“纵波”既可穿透固体又可以穿透液体。通过在地面对纵波和横波的反回情况,我们就可以大体断定地球内部的构造了。

大家注意到这两个例子的共同特点,就是从一个对象的外部去了解对象内部的构造,而且都是利用了波的反射功能。在.NET中的反射也可以实现从对象的外部来了解对象(或程序集)内部结构的功能,哪怕你不知道这个对象(或程序集)是个什么东西,另外.NET中的反射还可以运态创建出对象并执行它其中的方法

反射是.NET中的重要机制,通过反射,可以在运行时获得程序或程序集中每一个类型(包括类、结构、委托、接口和枚举等)的成员和成员的信息。有了反射,即可对每一个类型了如指掌。另外我还可以直接创建对象,即使这个对象的类型在编译时还不知道。
   
1.2 反射的用途
(1)使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。
(2)使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。
(3)使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。
(4)使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。
(5)使用FiedInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。
(6)使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。
(7)使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。
(8)使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。

1.3 反射用到的命名空间
    System.Reflection
    System.Type
    System.Reflection.Assembly

2、反射用到的主要类

System.Type 类          --通过这个类可以访问任何给定数据类型的信息。
System.Reflection.Assembly类  --它可以用于访问给定程序集的信息,或者把这个程序集加载到程序中。
   
2.1  System.Type类

  System.Type 类对于反射起着核心的作用。但它是一个抽象的基类,Type有与每种数据类型对应的派生类,我们使用这个派生类的对象的方法、字段、属性来查找有关该类型的所有信息。获取给定类型的Type引用有3种常用方式:
    ●使用 C# typeof 运算符。
        Type t = typeof(string);
    ●使用对象GetType()方法。
        string s = "grayworm";
        Type t = s.GetType();
    ●还可以调用Type类的静态方法GetType()。
        Type t = Type.GetType("System.String");

    上面这三类代码都是获取string类型的Type,在取出string类型的Type引用t后,我们就可以通过t来探测string类型的结构了。

            string n = "grayworm";
Type t = n.GetType();
foreach (MemberInfo mi in t.GetMembers())
{
Console.WriteLine("{0}/t{1}",mi.MemberType,mi.Name);
}

Type类的属性:
        Name     数据类型名
        FullName   数据类型的完全限定名(包括命名空间名)
        Namespace  定义数据类型的命名空间名
        IsAbstract    指示该类型是否是抽象类型
        IsArray      指示该类型是否是数组
        IsClass      指示该类型是否是类
        IsEnum     指示该类型是否是枚举
        IsInterface    指示该类型是否是接口
        IsPublic     指示该类型是否是公有的
        IsSealed    指示该类型是否是密封类
        IsValueType  指示该类型是否是值类型
    Type类的方法:
        GetConstructor(), GetConstructors():  返回ConstructorInfo类型,用于取得该类的构造函数的信息
        GetEvent(), GetEvents():        返回EventInfo类型,用于取得该类的事件的信息
        GetField(), GetFields():           返回FieldInfo类型,用于取得该类的字段(成员变量)的信息
        GetInterface(), GetInterfaces():      返回InterfaceInfo类型,用于取得该类实现的接口的信息
        GetMember(), GetMembers():         返回MemberInfo类型,用于取得该类的所有成员的信息
        GetMethod(), GetMethods():       返回MethodInfo类型,用于取得该类的方法的信息
        GetProperty(), GetProperties():       返回PropertyInfo类型,用于取得该类的属性的信息
    可以调用这些成员,其方式是调用Type的InvokeMember()方法,或者调用MethodInfo, PropertyInfo和其他类的Invoke()方法。
   
    查看类中的构造方法:

NewClassw nc = new NewClassw();
Type t = nc.GetType();
ConstructorInfo[] ci = t.GetConstructors(); //获取类的所有构造函数
foreach (ConstructorInfo c in ci) //遍历每一个构造函数
{
ParameterInfo[] ps = c.GetParameters(); //取出每个构造函数的所有参数
foreach (ParameterInfo pi in ps) //遍历并打印所该构造函数的所有参数
{
Console.Write(pi.ParameterType.ToString()+" "+pi.Name+",");
}
Console.WriteLine();
}

用构造函数动态生成对象:

Type t = typeof(NewClassw);
Type[] pt = new Type[2];
pt[0] = typeof(string);
pt[1] = typeof(string);
//根据参数类型获取构造函数
ConstructorInfo ci = t.GetConstructor(pt);
//构造Object数组,作为构造函数的输入参数
object[] obj = new object[2]{"grayworm","hi.baidu.com/grayworm"};
//调用构造函数生成对象
object o = ci.Invoke(obj);
//调用生成的对象的方法测试是否对象生成成功
//((NewClassw)o).show();

用Activator生成对象:

Type t = typeof(NewClassw);
//构造函数的参数
object[] obj = new object[] { "grayworm", "hi.baidu.com/grayworm" };
//用Activator的CreateInstance静态方法,生成新对象
object o=Activator.CreateInstance(t,"grayworm","hi.baidu.com/grayworm");
//((NewClassw)o).show();

查看类中的属性:

        NewClassw nc = new NewClassw();
Type t = nc.GetType();
PropertyInfo[] pis = t.GetProperties();
foreach(PropertyInfo pi in pis)
{
Console.WriteLine(pi.Name);
}

查看类中的public方法:

        NewClassw nc = new NewClassw();
Type t = nc.GetType();
MethodInfo[] mis = t.GetMethods();
foreach (MethodInfo mi in mis)
{
Console.WriteLine(mi.ReturnType+" "+mi.Name);
}

查看类中的public字段

        NewClassw nc = new NewClassw();
Type t = nc.GetType();
FieldInfo[] fis = t.GetFields();
foreach (FieldInfo fi in fis)
{
Console.WriteLine(fi.Name);
}

用反射生成对象,并调用属性、方法和字段进行操作

        NewClassw nc = new NewClassw();
Type t = nc.GetType();
object obj = Activator.CreateInstance(t);
//取得ID字段
FieldInfo fi = t.GetField("ID");
//给ID字段赋值
fi.SetValue(obj, "k001");
//取得MyName属性
PropertyInfo pi1 = t.GetProperty("MyName");
//给MyName属性赋值
pi1.SetValue(obj, "grayworm", null);
PropertyInfo pi2 = t.GetProperty("MyInfo");
pi2.SetValue(obj, "hi.baidu.com/grayworm", null);
//取得show方法
MethodInfo mi = t.GetMethod("show");
//调用show方法
mi.Invoke(obj, null);


2.2 System.Reflection.Assembly类

     Assembly类可以获得程序集的信息,也可以动态的加载程序集,以及在程序集中查找类型信息,并创建该类型的实例。
    使用Assembly类可以降低程序集之间的耦合,有利于软件结构的合理化。
   
    通过程序集名称返回Assembly对象
        Assembly ass = Assembly.Load("ClassLibrary831");
    通过DLL文件名称返回Assembly对象
        Assembly ass = Assembly.LoadFrom("ClassLibrary831.dll");
    通过Assembly获取程序集中类
        Type t = ass.GetType("ClassLibrary831.NewClass");   //参数必须是类的全名
    通过Assembly获取程序集中所有的类
        Type[] t = ass.GetTypes();
      
    //通过程序集的名称反射

    Assembly ass = Assembly.Load("ClassLibrary831");
Type t = ass.GetType("ClassLibrary831.NewClass");
object o = Activator.CreateInstance(t, "grayworm", "http://hi.baidu.com/grayworm");
MethodInfo mi = t.GetMethod("show");
mi.Invoke(o, null);

//通过DLL文件全名反射其中的所有类型

   Assembly assembly = Assembly.LoadFrom("xxx.dll的路径");
Type[] aa = a.GetTypes(); foreach(Type t in aa)
{
if(t.FullName == "a.b.c")
{
object o = Activator.CreateInstance(t);
}
}

3、一个利用反射获取属性值的较为详细的例子

3.1 例1

c# 如何通过反射获取属性值[2]

public class A
{
public int Property1 { get; set; }
}
static void Main(){
A aa = new A();
Type type = aa.GetType();//获取类型
System.Reflection.PropertyInfo propertyInfo = type.GetProperty("Property1");
propertyInfo.SetValue(aa, 5, null);//给对应属性赋值
int value = (int)propertyInfo.GetValue(aa, null);
Console.WriteLine(value );
}

3.2 例2

中等规模的类的反射方式获取属性值[3],这是一个控制台程序。具体代码如下。

学生字典类(S0001):

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Data;

namespace Dictionary.Class.S0001
{
/// <summary>
/// 学生小字典
/// </summary>
public class StudentDict
{ /// <summary>
/// 教师名
/// </summary>
private string Teacher = "张老师"; /// <summary>
/// 类名称
/// </summary>
public string ClassName = "高三(1)班"; /// <summary>
/// 简单字典类型
/// </summary>
public Dictionary<string, string> AttributeDict = new Dictionary<string, string>(); /// <summary>
/// 教师名称
/// </summary>
public string TeacherName
{
get { return Teacher; }
set { Teacher = value; }
} /// <summary>
/// 构造函数
/// </summary>
public StudentDict()
{
AttributeDict.Add("", "张三");
AttributeDict.Add("", "李四");
AttributeDict.Add("", "王五");
} /// <summary>
/// 根据序号查询姓名
/// </summary>
/// <param name="strCode"></param>
/// <returns></returns>
public string GetStuNameByCode(string strCode)
{
return AttributeDict[strCode];
} }
}

StudentDict 类1

学生字典类:(S0002):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data; namespace Dictionary.Class.S0002
{
/// <summary>
/// 学生小字典
/// </summary>
public class StudentDict
{ /// <summary>
/// 教师名
/// </summary>
private string Teacher = "曹老师"; /// <summary>
/// 类名称
/// </summary>
public string ClassName = "高三(2)班"; /// <summary>
/// 简单字典类型
/// </summary>
public Dictionary<string, string> AttributeDict = new Dictionary<string, string>(); /// <summary>
/// 教师名称
/// </summary>
public string TeacherName
{
get { return Teacher; }
set { Teacher = value; }
} /// <summary>
/// 构造函数
/// </summary>
public StudentDict()
{
AttributeDict.Add("", "赵六");
AttributeDict.Add("", "钱七");
AttributeDict.Add("", "周八");
} /// <summary>
/// 根据序号查询姓名
/// </summary>
/// <param name="strCode"></param>
/// <returns></returns>
public string GetStuNameByCode(string strCode)
{
return AttributeDict[strCode];
}
}
}

StudentDict 类2

主函数,Program主类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Reflection; namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
Type type = assembly.GetType("Dictionary.Class.S0002.StudentDict"); //命名空间名 + 类名
object obj = Activator.CreateInstance(type, true);
try
{
FieldInfo classField = type.GetField("ClassName");
Console.WriteLine("班级名称:" + classField.GetValue(obj).ToString());
}
catch (Exception ex)
{
Console.WriteLine("\t班级名称获取失败:" + ex.Message);
}
try
{
PropertyInfo TeaNameProperty = type.GetProperty("TeacherName");
Console.WriteLine("\t教师姓名:" + TeaNameProperty.GetValue(obj, null).ToString());
}
catch (Exception ex)
{
Console.WriteLine("\t教师姓名获取失败:" + ex.Message);
}
foreach (FieldInfo field in type.GetFields())
{
try
{
if (field.Name == "AttributeDict")
{
Dictionary<string, string> dict = field.GetValue(obj) as Dictionary<string, string>;
foreach (string key in dict.Keys)
{
Console.WriteLine("\t\t学号:{0} -> 姓名:{1}", key, dict[key]);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message.ToString());
}
}
MethodInfo method = type.GetMethod("GetStuNameByCode");
string strStuName = (string)method.Invoke(obj, new string[] { "" });
Console.WriteLine("\t\t学号【{0}】 的学生姓名为:{1}","",strStuName);
Console.ReadLine();
}
}
}
Program

Program

3.3 例3

3.3.1

//定义类
public class MyClass
{
public int Property1 { get; set; }
}
static void Main()
{
MyClass tmp_Class = new MyClass();
tmp_Class.Property1 = 2;
Type type = tmp_Class.GetType(); //获取类型
System.Reflection.PropertyInfo propertyInfo = type.GetProperty("Property1"); //获取指定名称的属性
int value_Old = (int)propertyInfo.GetValue(tmp_Class, null); //获取属性值
Console.WriteLine(value_Old);
propertyInfo.SetValue(tmp_Class, 5, null); //给对应属性赋值
int value_New = (int)propertyInfo.GetValue(tmp_Class, null);
Console.WriteLine(value_New); }

3.3.2  利用反射获取对象属性值

public static string GetObjectPropertyValue<T>(T t, string propertyname)
{
Type type = typeof(T);
PropertyInfo property = type.GetProperty(propertyname);
if (property == null) return string.Empty;
object o = property.GetValue(t, null);
if (o == null) return string.Empty;
return o.ToString();
}

参考博文

[1] JavaCodingMan. 详解C#中的反射 ,  2008-9.

[2] 奋斗一生jin. c# 如何通过反射 获取属性值,  2012-5.

[3] 一路前行, C#反射(取得方法、属性、变量), 2012-3.

接触C# 反射的更多相关文章

  1. [转] 接触C# 反射 2

    逆心 原文 反射基础,2013-4. 反射用于在程序运行过程中,获取类里面的信息或发现程序集并运行的一个过程.通过反射可以获得.dll和.exe后缀的程序集里面的信息.使用反射可以看到一个程序集内部的 ...

  2. .Net 中的反射(序章) - Part.1

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

  3. C#反射(二) 【转】

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

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

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

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

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

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

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

  7. PhysicalBasedRendering(一)物理篇

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

  8. C#提高-------------------Module的使用

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

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

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

随机推荐

  1. js之广告弹出自动关闭

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

  2. HDU2222 Keywords Search ac自动机第一题

    指针我一般都会出错,所以还是自己写数组版本. In the modern time, Search engine came into the life of everybody like Google ...

  3. maven搭建ssm框架问题总结

    1. Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.6.0:compile (default-comp ...

  4. bzoj 4447 小凸解密码

    bzoj 4447 小凸解密码 先将原始状态的 \(B\) 处理出来,可以发现,若不修改,则每次指定的起始位置不同,对这个环 \(B\) 带来的影响只有 \(B_0\) 不同,即每次 \(B_0=A_ ...

  5. python笔记-19 javascript补充、web框架、django基础

    一.JavaScript的补充 1 正则表达式 1.1 test的使用 test 测试是否符合条件 返回true or false 1.2 exec的使用 exec 从字符串中截取匹配的字符 1.3 ...

  6. altera官方推荐时钟使用方法

    Register Combinational Logic Outputs If you use the output from combinational logic as a clock signa ...

  7. php替换 json符号

  8. C#中的依赖注入那些事儿

    目录 目录 1 IGame游戏公司的故事 1.1 讨论会 1.2 实习生小李的实现方法 1.3 架构师的建议 1.4 小李的小结 2 探究依赖注入 2.1 故事的启迪 2.2 正式定义依赖注入 3 依 ...

  9. 在Eclipse中配置Tomcat7.0

    为了在Eclipse中进行struts2的测试,才发现自己机器上的Eclipse没有集成Tomcat,在网上找了半天,不是这个插件没有下载地址,就是那个有好多注意事项或者版本问题.结果,自己到tomc ...

  10. angular(mvc)指令的嵌套使用

    关于指令嵌套的使用,取值问题. 原理类似于控制器中使用指令,父指令类似于控制器,子指令就类似于控制器中指令.通过传值方式‘=’,我们直接可以在父指令中获取数据 举一个例子: 有个指令parentDir ...