谈谈对C#中反射的一些理解和认识(上)

时间:2023-03-09 08:36:43
谈谈对C#中反射的一些理解和认识(上)

  今天就平常用到的非常多的反射这个技术来做一个总结,当然关于反射需要讲解的东西实在是太多的内容,在一片文章中想要讲解清楚是非常难的,本篇博客也是就自己本人对这些内容学习后的一个总结,当然包括看书和自己写过的一些代码中抽取的一些示例,而且本文也仅限于此时对于这个知识点的理解,希望通过以后的逐步学习能够不断加深对这个知识点的理解。

  首先来看看对于反射的基础知识点。

1 定义:首先看看MSDN怎样对它进行解释吧

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

  2 反射有什么作用?

 A、将类型绑定到现有对象,或从现有对象中获取类型信息,这些信息包括(Assembly   MemberInfo  EventInfo  FieldInfo  MethodBase  ConstructorInfo  MethodInfo  PropertyInfo 等等 )另外可以使用反射动态地创建类型的实例,

        例如:
      1、System.Activator 的CreateInstance方法。该方法返回新对象的引用。
      2、System.Activator 的CreateInstanceFrom 与上一个方法类似,不过需要指定类型及其程序集
      3、System.Appdomain 的方法:CreateInstance,CreateInstanceAndUnwrap,CreateInstranceFrom和CreateInstraceFromAndUnwrap
      4、System.Type的InvokeMember 实例方法:这个方法返回一个与传入参数相符的构造函数,并构造该类型。
      5、System.Reflection.Constructinfo 的Invoke实例方法

     下面通过一段代码来了解这5种创建实例的方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Diagnostics;
using OptimizeReflection;
using System.Collections;
using System.Web; namespace TestOptimizeReflection
{
public class OrderInfo
{
public int OrderID { get; set; }
public DateTime OrderDate { get; set; }
public decimal SumMoney { get; set; }
public string Comment { get; set; }
public bool Finished { get; set; } public int Add(int a, int b)
{
return a + b;
}
} class Program
{
static void Main()
{
TestNewInstance();
} static void TestNewInstance()
{
//常规方法创建对象
OrderInfo testObj = new OrderInfo(); #region 常规反射创建
//利用反射来动态创建对象
Type instanceType = typeof(OrderInfo);
OrderInfo orderInfo = (OrderInfo)Activator.CreateInstance(instanceType);
#endregion #region Activator.CreateInstanceFrom 创建
//注意Assembly.GetEntryAssembly().CodeBase表示当前执行的exe所在的路径,instanceType.FullName表示当前OrderInfo的类型名称TestOptimizeReflection.OrderInfo
System.Runtime.Remoting.ObjectHandle oh= Activator.CreateInstanceFrom(Assembly.GetEntryAssembly().CodeBase, instanceType.FullName);
//返回被包装的对象
OrderInfo orderInfoEx = (OrderInfo)oh.Unwrap();
#endregion #region System.AppDomain.CurrentDomain实例创建对象
System.Runtime.Remoting.ObjectHandle ohEx = System.AppDomain.CurrentDomain.CreateInstance(Assembly.GetEntryAssembly().FullName, instanceType.FullName);
OrderInfo orderInfoExEx = (OrderInfo)ohEx.Unwrap();
//合并上面的两步
OrderInfo orderInfoExExEx = (OrderInfo)System.AppDomain.CurrentDomain.CreateInstanceAndUnwrap(Assembly.GetEntryAssembly().FullName, instanceType.FullName);
#endregion #region InvokeMember方法创建实例
OrderInfo invokeMemberOrderInfo = (OrderInfo)instanceType.InvokeMember(null, BindingFlags.Public| BindingFlags.Instance | BindingFlags.Static|BindingFlags.CreateInstance, System.Type.DefaultBinder, null, null);
#endregion #region 调用构造器创建
//调用无参数的默认构造函数
ConstructorInfo ci = instanceType.GetConstructor(new Type[] { });
OrderInfo ciOrderInfo = (OrderInfo)ci.Invoke(null);
#endregion
}
}
}  

    B、应用程序需要在运行时从某个特定的程序集中载入一个特定的类型,以便实现某个任务时可以用到反射。

   这个该怎样去理解呢?这个可以用插件系统中的同类思想去解释,在构建插件系统的时候,我们有时候需要主程序去动态地调用插件,可能应用程序只有在执行某一操作的时候才能够去调用相关的DLL,这个时候我们就可以通过反射这种方式来动态调用dll中分特定方法或者类型,这个也是经常使用到的。
    C、反射主要应用与类库,这些类库需要知道一个类型的定义,以便提供更多的功能。

    D、反射能够调用一些私有方法和字段等。

        这个需要重点去讲述,我们知道常规的实例方法由于受到作用域的影响,很多时候当方法设置为Private或者是其它的限制访问的关键字时就无能为力了,这个也是完全能够体现类的封装性,但是通过反射就能够完全绕开这些限制,下面举出一些对私有变量或字段、方法的一些访问方式,能够直接进行访问,这样还是非常方便的,具体请参考下面的代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks; namespace Dvap.Infrastructure.Utils
{
public static class ReflectionUtil
{
/// <summary>
/// 得到私有字段的值
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="instance"></param>
/// <param name="fieldname"></param>
/// <returns></returns>
public static T GetPrivateField<T>(this object instance, string fieldname)
{
BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
Type type = instance.GetType();
FieldInfo field = type.GetField(fieldname, flag);
return (T)field.GetValue(instance);
} /// <summary>
/// 设置私有成员的值
/// </summary>
/// <param name="instance"></param>
/// <param name="fieldname"></param>
/// <param name="value"></param>
public static void SetPrivateField(this object instance, string fieldname, object value)
{
BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
Type type = instance.GetType();
FieldInfo field = type.GetField(fieldname, flag);
field.SetValue(instance, value);
} /// <summary>
/// 得到私有属性的值
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="instance"></param>
/// <param name="propertyname"></param>
/// <returns></returns>
public static T GetPrivateProperty<T>(this object instance, string propertyname)
{
BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
Type type = instance.GetType();
PropertyInfo field = type.GetProperty(propertyname, flag);
return (T)field.GetValue(instance, null);
} /// <summary>
/// 设置私有属性的值
/// </summary>
/// <param name="instance"></param>
/// <param name="propertyname"></param>
/// <param name="value"></param>
public static void SetPrivateProperty(this object instance, string propertyname, object value)
{
BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
Type type = instance.GetType();
PropertyInfo field = type.GetProperty(propertyname, flag);
field.SetValue(instance, value, null);
} /// <summary>
/// 调用私有方法
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="instance"></param>
/// <param name="name"></param>
/// <param name="param"></param>
/// <returns></returns>
public static T CallPrivateMethod<T>(this object instance, string name, params object[] param)
{
BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
Type type = instance.GetType();
MethodInfo method = type.GetMethod(name, flag);
return (T)method.Invoke(instance, param);
}
}
}

    E 还可以通过反射来获取属性或者类上的特性

    有时候我们需要在类或者属性上面添加自定义的特性,并且通过反射能够获取到这些特性,那么这个时候我们就可以使用反射来达到目的了。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Diagnostics;
using OptimizeReflection;
using System.Collections;
using System.Web; namespace TestOptimizeReflection
{ /// <summary>
/// 自定义特性 属性或者类可用 支持继承
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class, Inherited = true)]
public class EnitityMappingAttribute : Attribute
{
private string tableName;
/// <summary>
/// 实体实际对应的表名
/// </summary>
public string TableName
{
get { return tableName; }
set { tableName = value; }
} private string columnName;
/// <summary>
/// 中文列名
/// </summary>
public string ColumnName
{
get { return columnName; }
set { columnName = value; }
}
} /// <summary>
/// 会员 ,实际的表名叫MemberInfo,并不是和实体名一致
/// </summary>
[EnitityMapping(TableName = "MemberInfo")]
public class Member
{
private int id;
[EnitityMapping(ColumnName = "关键字")]
public int Id
{
get { return id; }
set { id = value; }
} private string userName;
[EnitityMapping(ColumnName = "会员注册名")]
public string UserName
{
get { return userName; }
set { userName = value; }
} private string realName;
[EnitityMapping(ColumnName = "会员真实名")]
public string RealName
{
get { return realName; }
set { realName = value; }
} private bool isActive;
/// <summary>
/// 是否活跃 没有附加自定义属性
/// </summary>
public bool IsActive
{
get { return isActive; }
set { isActive = value; }
}
} class Program
{
/// <summary>
/// 通过反射取自定义属性
/// </summary>
/// <typeparam name="T"></typeparam>
private static void DisplaySelfAttribute<T>() where T : class, new()
{
string tableName = string.Empty;
List<string> listColumnName = new List<string>();
Type objType = typeof(T);
//取属性上的自定义特性
foreach (PropertyInfo propInfo in objType.GetProperties())
{
object[] objAttrs = propInfo.GetCustomAttributes(typeof(EnitityMappingAttribute), true);
if (objAttrs.Length > 0)
{
EnitityMappingAttribute attr = objAttrs[0] as EnitityMappingAttribute;
if (attr != null)
{
listColumnName.Add(attr.ColumnName); //列名
}
}
} //取类上的自定义特性
object[] objs = objType.GetCustomAttributes(typeof(EnitityMappingAttribute), true);
foreach (object obj in objs)
{
EnitityMappingAttribute attr = obj as EnitityMappingAttribute;
if (attr != null)
{
tableName = attr.TableName;//表名只有获取一次
break;
}
}
if (string.IsNullOrEmpty(tableName))
{
tableName = objType.Name;
}
Console.WriteLine(string.Format("The TableName of the entity is:{0} ", tableName));
if (listColumnName.Count > 0)
{
Console.WriteLine("The Columns of the table are as follows:");
foreach (string item in listColumnName)
{
Console.WriteLine(item);
}
}
} static void Main()
{
DisplaySelfAttribute<Member>(); //显示结果
Console.ReadLine();
}
}
}

  最终呈现的效果:

谈谈对C#中反射的一些理解和认识(上)

  这里简单说一下AttributeUsage的一些用法,AttributeUsage 有三个 属性 ,分别是

  public bool AllowMultiple { get; set; }   作用:是否能在一个目标身上多次使用

   public bool Inherited { get; set; } 作用 :特性是否能继承到子类身上

  public AttributeTargets ValidOn { get; } 作用:设置特性的可使用范围

  这里面就是要解释一下AttributeTargets 的特性使用范围,这个主要是用在继承自Attribute的子类里面,在本例中我们自定义的特性主要用于标识类和属性

public enum AttributeTargets
{
// 摘要:
// 可以对程序集应用特性。
Assembly = 1,
//
// 摘要:
// 可以对模块应用特性。
Module = 2,
//
// 摘要:
// 可以对类应用特性。
Class = 4,
//
// 摘要:
// 可以对结构应用特性,即值类型。
Struct = 8,
//
// 摘要:
// 可以对枚举应用特性。
Enum = 16,
//
// 摘要:
// 可以对构造函数应用特性。
Constructor = 32,
//
// 摘要:
// 可以对方法应用特性。
Method = 64,
//
// 摘要:
// 可以对属性应用特性。
Property = 128,
//
// 摘要:
// 可以对字段应用特性。
Field = 256,
//
// 摘要:
// 可以对事件应用特性。
Event = 512,
//
// 摘要:
// 可以对接口应用特性。
Interface = 1024,
//
// 摘要:
// 可以对参数应用特性。
Parameter = 2048,
//
// 摘要:
// 可以对委托应用特性。
Delegate = 4096,
//
// 摘要:
// 可以对返回值应用特性。
ReturnValue = 8192,
//
// 摘要:
// 可以对泛型参数应用特性。
GenericParameter = 16384,
//
// 摘要:
// 可以对任何应用程序元素应用特性。
All = 32767,
}