C#语法——反射,架构师的入门基础。

前言

反射

编程其实就是写代码,而写代码目的就是实现业务,所以,语法和框架也是为了实现业务而存在的。因此,不管多么高大上的目标,实质上都是业务。

简介

  反射是 .NET中的重要机制,通过反射,可以在运行时获得程序或程序集中类型(包括 class、struct、delegate、interface 和 enum 等)的成员和成员的信息。

  通过反射,即可对每一种类型了如指掌,并且也可以通过反射创建、调用和访问对象,即便在编译时不确定该对象的类型。

  程序集包含模块,模块包含类型,而类型包含成员。反射提供封装程序集、模块和类型的对象。可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。

所以,我认为不要把写代码上升到科学的高度。上升到艺术就可以了,因为艺术本身也没有高度。。。。

优缺点

  优点:

  1. 提高了程序的灵活性和扩展性;
  2. 降低耦合性;
  3. 它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

  缺点:

  1. 性能:使用反射基本上是一种解释操作,从理论上讲使用反射远慢于直接代码;
  2. 可读性降低。

软件设计存在过度设计,语法和框架的理解,也存在过度理解。比如,反编译下,看看反射是怎么实现的。。。

反射的类型成员信息

  • Assembly:定义和加载程序集。

  • Module:模块信息(如包含模块的程序集和模块中的类)。

  • ConstructorInfo:构造函数信息(如名称、参数、访问修饰符等)。
  • MethodInfo:方法成员信息(如名称、返回类型、参数和访问修饰符等)。

  • FieldInfo:字段成员信息(如名称、访问修饰符)。

  • EventInfo:事件成员信息(如名称、事件处理程序的数据类型、自定义特性、声明类型以及事件的反射的类型)。

  • PropertyInfo:属性成员信息(如名称、数据类型、声明类型,反射的类型和属性的只读或可写状态),并获取或设置属性值。

  • ParameterInfo:参数成员信息(如参数名、数据类型以及参数在方法签名中的位置等)。

  • CustomAttributeData:自定义特性信息。

  System.Reflection.Emit命名空间的类提供一种专用形式的反射,使你能够在运行时生成类型。

有兴趣是好事,但就算知道了反射的本质,了解了反射是如何设计的,你技术也没什么质的改变。因为,技术水平最终还是要落实到应用上。

反射的简单用法

  命名空间:System.Reflection、System.Type、System.Reflection.Assembly

  常见的获取 Type 对象的用法:

            Type type1 = typeof(string);            string msg = "";            Type type2 = msg.GetType();

在比如,过度的追求代码性能,也不见得是一件好事,因为,[大多数]情况下,硬件比程序员便宜多了。。。(注意这里指的是代码不是算法和数据库性能)

一个常见的示例用法

  我们一开始学习三层架构的时候,都应该会自己跟着老师动手打造一个 SqlHelper 的吧,这里我截取一个通过反射读取数据库数据并填充到一个对象的属性上,通过循环遍历,最后生成一个 list 列表的代码。

        /// <summary>        /// 执行 Reader 并读取数据转换成集合        /// </summary>        /// <typeparam name="T"></typeparam>        /// <param name="sql"></param>        /// <param name="commandType"></param>        /// <param name="parameters"></param>        /// <returns></returns>        public static IList<T> ExecuteReaderToList<T>(string sql, CommandType commandType = CommandType.Text,            params SqlParameter[] parameters) where T : new()        {            var type = typeof;            var props = type.GetProperties();            var list = new List<T>();            using (var reader = ExecuteDataReader(sql, commandType, parameters))            {                while (reader.Read                {                    var entity = new T();                    foreach (var propertyInfo in props)                    {                        var schemaTable = reader.GetSchemaTable();                        if (schemaTable == null)                            return new List<T>();                        schemaTable.DefaultView.RowFilter = $"ColumnName='{propertyInfo.Name}'";                        if (schemaTable.DefaultView.Count <= 0) continue;                        if (!propertyInfo.CanWrite)                            continue;                        var val = reader[propertyInfo.Name];                        if (val != DBNull.Value)                            propertyInfo.SetValue(entity, val);                    }                    list.Add;                }            }            return list;        }

  简单分析使用反射的代码:

    type.GetProperties():获取属性集合;

    propertyInfo.CanWrite:可写属性;

    propertyInfo.SetValue(entity, val):属性赋值,选择对应的对象进行赋值。


反骨仔

http://www.cnblogs.com/liqingwen/p/6242357.html

微软官方文档

所以,不论什么事,过度了,总不是好事。


本篇文章主要介绍C#反射【用法】。

反射是架构师必会的基础,因为任何一个被设计出来的框架,都要使用反射。

反射也是最隐蔽的语法,因为反射写出来后,通常它会被直接封装,然后调用者就只负责使用,不再关注他的具体实现。

这与它的特性有关,因为反射就是为了减少代码冗余而存在的,所以,看不见很正常。

反射的定义

官方定义:反射提供了封装程序集、模块和类型的对象(Type 类型)。可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。如果代码中使用了属性,可以利用反射对它们进行访问。

看不懂?没关系,我们把它翻译成人类可理解的语言。

C#编程语言中,最常使用的是类和类中的函数和属性。正向调用的方法是,创建类,然后用类创建一个对象。接下来就可以用这个对象调用类中的方法和属性了。

而反射,就是相对于这种正向调用的存在。即,它是反向调用。

反射可以通过类名的字符串来创建类,可以通过函数名的字符串和属性名的字符串,来调用类下的函数和属性。

有同学会问了, 既然正向可以调用,那么反向调用干什么呢?

会有这种问题的同学,先别着急,继续往下看,反射既然存在,就必然有存在的道理。

反射的基础应用

金沙官网线上,1,类反射

先看下面代码;代码为通过类名称的字符,反射出类的对象。

public class ReflectionSyntax
{ 
    public static void Excute()
    {
        Type type = GetType("Syntax.Kiba");
        Kiba kiba = (Kiba)Activator.CreateInstance(type);
        Type type2 = GetType2("Syntax.Kiba");
        Kiba kiba2 = (Kiba)Activator.CreateInstance(type2);
    }
    public static Type GetType(string fullName)
    {
        Assembly assembly = Assembly.Load("Syntax");
        Type type = assembly.GetType(fullName, true, false);
        return type;
    }

    public static Type GetType2(string fullName)
    {
        Type t = Type.GetType(fullName);
        return t;
    } 
} 
public class Kiba
{ 
    public void PrintName()
    {
        Console.WriteLine("Kiba518");
    } 
} 

在代码中我们看到,反射时传递了字符串"Syntax.Kiba",然后通过解析字符串,获取到了该字符串对应的类的类型,最后再借助Activator来辅助创建类的实例。

其中字符串"Syntax.Kiba"是一个完全限定名。什么是完全限定名?完全限定名就是命名空间+类名。在反射的时候,需要我们传递完全限定名来确定到底要去哪个命名空间,找哪个类。

在代码中我们还可以看到,获取类型的方式有两种,一种是较复杂的,一种是简单的。

GetType2方法是简单的获取类别,通过Type直接就解析了字符串。而GetType则先进行了加载Assembly(组件),然后再由组件获取类型。

两者有什么区别呢?

区别是,用Type直接解析,只能解析当前命名空间下的类。如果该类存在于引用的DLL中,就解析不了。

而GetType方法中的[Assembly.Load指定了程序集名],所以,在反射时,就会去指定的命名空间里找对应的类。这样就能找到非本程序集下的类了。

[Assembly.Load指定了程序集名]这句话不好理解?

没关系,换个表达,Assembly.Load指定了命名空间的名称,所以反射时,会去这个命名空间里找类,这样是不是就好理解了。

Assembly

Assembly的存在让反射变得特别灵活,其中Assembly.Load不止可以导入我们引入的程序集(或命名空间)。

也可以导入我们未引入程序集的dll。调用模式如下:

System.Reflection.Assembly o = System.Reflection.Assembly.Load("mscorlib.dll");

Assembly导入了程序集后,还可以不借助Activator来辅助,自己就可以创建类。如下:

Assembly assembly = Assembly.Load("Syntax");
Kiba kiba = (Kiba)assembly.CreateInstance("Syntax.Kiba");

有的同学可能会担心性能,会觉得这样反射,会使程序变慢。

有这种想法的同学,其实你已经是在过度理解语法了。这种地方的代码性能其实是可以不用关心的。

那么,到底会不会变慢呢?

答案是这样的,如果你是使用完全限定名来反射,速度就是一样的。如果是反射时,只写了一个类名,那么速度就会变慢。因为它要遍历所有的命名空间,去找这个类。

即,只要反射时把类的命名空间写全,那么速度就不会慢。

2,函数反射

函数的反射应用主要是使用类MethodInfo类反射,下面先看下基础应用。

public static void ExcuteMethod()
{ 
    Assembly assembly = Assembly.Load("Syntax"); 
    Type type = assembly.GetType("Syntax.Kiba", true, false);
    MethodInfo method =  type.GetMethod("PrintName"); 
    object kiba = assembly.CreateInstance("Syntax.Kiba");
    object[] pmts = new object[] { "Kiba518" };
    method.Invoke(kiba, pmts);//执行方法  
}
public class Kiba
{
    public string Name { get; set; }
    public void PrintName(string name)
    {
        Console.WriteLine(name);
    }
}

一些同学第一眼看上去可能会有点不适应,因为好像很多类都是大家不经常用的。这也没办法,因为这是一个进阶的过程,必须经历从陌生到熟悉。当你熟悉了这样的代码后,就代表你的技术水平又进步了一个台阶。

下面讲解一些这些代码。

首先我们导入了命名空间,接着我们获取了该命名空间下Kiba这个类的类型;接下来我们通过这个类型来获取指定名称的函数。

然后我们通过Assembly创建了一个Kiba的实例,接着定义了一个参数的Object数组,因为Kiba类下的函数PrintName只有一个参数,所以,我们只为这个Object数组添加一个对象[Kiba518]。

最后,我们通过method.Invoke来调用这个函数,由于是反射,所以调用时,需要指定Kiba类的实例对象和入参。

这样,函数的反射就实现了。

3,属性反射

属性反射是用PropertyInfo类来实现,下面看基础的属性反射。

public static void ExcuteProperty()
{
    Kiba kiba = new Kiba();
    kiba.Name = "Kiba518";
    object name = ReflectionSyntax.GetPropertyValue(kiba, "Name");
    Console.WriteLine(name); 
} 
public static object GetPropertyValue(object obj, string name)
{
    PropertyInfo property = obj.GetType().GetProperty(name);
    if (property != null)
    {
        object drv1 = property.GetValue(obj, null);
        return drv1;
    }
    else
    {
        return null;
    } 
}

如代码所示,首先我们定义了一个Kiba的对象,并为Name赋值,然后我们通过GetPropertyValue方法,传递了Kiba对象和要获取值的属性名称。

GetPropertyValue函数里通过使用PropertyInfo完成了反射。

本文由金沙官网线上发布于编程,转载请注明出处:C#语法——反射,架构师的入门基础。

您可能还会对下面的文章感兴趣: