欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解C#特性和反射(二)

程序员文章站 2022-05-20 12:03:56
使用反射(Reflection)使得程序在运行过程中可以动态的获取对象或类型的类型信息,然后调用该类型的方法和构造函数,或访问和修改该类型的字段和属性;可以通过晚期绑定技术动态的创建类型的实例;可以获取程序集中的所有类型信息;可以在动态构建新类型;还可以检索元素所添加的特性; ※反射相关的类基本都位 ......

  使用反射(reflection)使得程序在运行过程中可以动态的获取对象或类型的类型信息,然后调用该类型的方法和构造函数,或访问和修改该类型的字段和属性;可以通过晚期绑定技术动态的创建类型的实例;可以获取程序集中的所有类型信息;可以在动态构建新类型;还可以检索元素所添加的特性;
  ※反射相关的类基本都位于命名空间system.reflection中;
  ※动态构建新类型的类位于命名空间system.reflection.emit中;


  一、访问或修改类型的实例、静态字段:

public class myclass
{
    public int myfield;
    public static int mystaticfield;
}

//使用方式:
//访问或修改类型的实例字段myfield
myclass myobj = new myclass() { myfield = 1 }; //创建实例
type mytype = typeof(myclass); //获取类型,或myobj.gettype()
fieldinfo fieldinfo = mytype.getfield("myfield"); //获取类型中指定的字段信息
console.writeline((int)fieldinfo.getvalue(myobj)); //1,获取实例字段的值
fieldinfo.setvalue(myobj, 2); //给实例字段赋值
//访问或修改类型的静态字段mystaticfield
fieldinfo staticfieldinfo = mytype.getfield("mystaticfield"); //获取类型中指定的字段信息
console.writeline(staticfieldinfo.getvalue(null)); //0,获取静态字段的值
staticfieldinfo.setvalue(null, 2); //给静态字段赋值

  ※与直接赋值相比,使用反射赋值用时约长75倍,使用以下代码多次测试:

public class myclass
{
    public int myfield;
}

class program
{
    static void main(string[] args)
    {
        stopwatch stopwatch = new stopwatch();
        myclass myobj = new myclass() { myfield = 1 };
        type mytype = typeof(myclass);
        fieldinfo fieldinfo = mytype.getfield("myfield");

        stopwatch.start();
        for (int i = 0; i < 10_000_000; i++)
        {
            fieldinfo.setvalue(myobj, 2);
        }
        stopwatch.stop();
        console.writeline($"使用反射赋值1千万次耗时:{stopwatch.elapsedmilliseconds}");

        stopwatch.reset();
        stopwatch.start();
        for (int i = 0; i < 10_000_000; i++)
        {
            myobj.myfield = 2;
        }
        stopwatch.stop();
        console.writeline($"直接赋值1千万次耗时:{stopwatch.elapsedmilliseconds}");
        console.read();
    }
}

  二、访问或修改类型的实例、静态属性:

public class myclass
{
    public int myproperty { get; set; }
    public static int mystaticproperty { get; set; }
}
//使用方式: //访问或修改类型的实例属性myproperty myclass myobj = new myclass() { myproperty = 1 }; //创建实例 type mytype = typeof(myclass); //获取类型,或myobj.gettype() propertyinfo propertyinfo = mytype.getproperty("myproperty"); //获取类型中指定的属性信息 console.writeline((int)propertyinfo.getvalue(myobj, null)); //1,获取实例属性的值 propertyinfo.setvalue(myobj, 2, null); //给实例属性赋值 //访问或修改类型的静态属性mystaticproperty propertyinfo staticpropertyinfo = mytype.getproperty("mystaticproperty"); //获取类型中指定的属性信息 console.writeline(staticpropertyinfo.getvalue(null, null)); //0,获取静态属性的值 staticpropertyinfo.setvalue(null, 2); //给静态属性赋值

  ※在使用反射给属性赋值时,如果该属性不具有set访问器,则会抛出异常argumentexception;

  三、调用类型的方法:

public class myclass
{
  public void myfunc(int num)
  {
    console.writeline("myfunc(int num) execute, the parameter is: " + num);
  }
  public static void mystaticfunc(int num)
  {
      console.writeline("mystaticfunc(int num) execute, the parameter is: " + num);
  }
}
//使用方式: //调用类型的实例方法myfunc myclass myobj = new myclass(); //创建实例 type mytype = typeof(myclass); //获取类型,或myobj.gettype() methodinfo methodinfo = mytype.getmethod("myfunc"); //获取类型中指定的方法信息 methodinfo.invoke(myobj, new object[] { 10 }); //调用实例方法,并传入参数,无参传null //myfunc(int num) execute, the parameter is: 10 //调用类型的实例方法mystaticfunc methodinfo staticmethodinfo = mytype.getmethod("mystaticfunc"); //获取类型中指定的方法信息 staticmethodinfo.invoke(null, new object[] { 20 }); //调用静态方法,并传入参数,无参传null //mystaticfunc(int num) execute, the parameter is: 20

  四、调用类型的构造函数同时创建实例:

public class myclass
{
    public myclass()
    {
        console.writeline("myclass() execute.");
    }
    public myclass(int num)
    {
        console.writeline("myclass(int num) execute, the parameter is: " + num);
    }
}
//使用方式: //调用无参的构造函数 type mytype = typeof(myclass); //获取类型,或myobj.gettype() constructorinfo constructorinfo = mytype.getconstructor(new type[] { }); //获取类型中指定的构造函数信息,传入该构造函数的参数列表的类型数组,无参传空数组 myclass myobj = constructorinfo.invoke(null) as myclass; //通过调用构造函数创建实例,无参传null //myclass() execute. //调用带参数的构造函数 constructorinfo = mytype.getconstructor(new type[] { typeof(int) }); //获取类型中指定的构造函数信息,传入该构造函数的参数列表的类型数组 myobj = constructorinfo.invoke(new object[] { 20 }) as myclass; //通过调用构造函数创建实例,并传入参数 //myclass(int num) execute, the parameter is: 20

  ※也可以使用type类中的实例方法invokemember()来调用指定成员;

  五、使用反射查找特性:

  1.如果元素使用了特性,在没有检索并对其进行操作前该特性没有任何价值,可以使用反射在程序运行过程中获取元素添加的特性然后对其进行操作,使用特性基类attribute中的静态方法getcustomattributes(memberinfo element)或命名空间system.reflection中的扩展方法getcustomattributes(this memberinfo element)来获取类型或成员的所有特性信息:

attribute[] attributes = attribute.getcustomattributes(typeof(myclass));
//ienumerable<attribute> attributes = typeof(myclass).getcustomattributes();
foreach (var item in attributes)
{
  if (item is myselfattribute)
  {
    myselfattribute attribute = item as myselfattribute;
    console.writeline(attribute .classname + " " + attribute .author); //myclass me
  }
}

  2.这两个方法都有对应的重载方法,可以传入要检索的指定特性的类型,这样即可得到元素中所有指定类型的特性信息:

attribute[] attributes = attribute.getcustomattributes(typeof(myclass), typeof(myselfattribute));
//ienumerable<attribute> attributes = typeof(myclass).getcustomattributes(typeof(myselfattribute));
foreach (var item in attributes)
{
  myselfattribute attribute = item as myselfattribute;
  console.writeline(attribute.classname + " " + attribute.author); //myclass me
}

  ※如果未找到任何特性或指定类型的特性,这些方法会返回一个空数组;

  3.也可以使用基类attribute中的静态方法getcustomattribute(memberinfo element, type attributetype)或命名空间system.reflection中的扩展方法getcustomattribute(this memberinfo element, type attributetype)来获取类型或成员的指定特性信息;
  ※如果未找到指定类型的特性,会返回null;
  ※在检索的元素中存在多个相同的指定类型的特性时,会抛出异常reflection.ambiguousmatchexception;

 

  类型信息、晚期绑定、动态创建类型等会在下一篇中介绍。

 


 

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的认可是我写作的最大动力!

作者:minotauros
出处:

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。