C# 反射

75 阅读3分钟

定义

反射---程序可以访问、检测和修改它本身状态或行为的一种能力。反射是.NET中的重要机制。

反射中提供了用来描述程序集、模块和类型的对象,可以使用反射动态地创建类型的实例,并将类型绑定到现有对象,或者从现有对象中获取类型,然后调用其方法或访问其字段和属性。如果代码中使用了特性,也可以利用反射来访问它们。

通过反射,可以在运行时获得程序或程序集中每一个类型(包括类、结构、委托、接口和枚举等)的成员和成员的信息,即元数据。有了反射,即可对每一个类型了如指掌。

反射用到的类:System.Type、System.Relection.Assembly、Activator

Type:类的信息类,可以获取类的相关信息(构造函数、方法、字段、属性、事件等)

Assembly:程序集类,加载其他程序集,加载后能用Type获取其中某类的信息

Activator:快速实例化对象的类,可将Type对象实例化为相应类型的对象

用途

C# 中反射具有以下用途:

  • 在运行时查看类型元数据;
  • 检查装配中的各种类型并动态实例化这些类型;
  • 运行时动态读写对象的属性值,调用方法、动态加载程序集;
  • 运行时访问应用的特性

基础使用

动态实例化

类型实例化,除了可以直接使用类型实例化创建对象以外,当不能直接引用类型时,还可以通过反射技术来动态实例化。

string fullName ="Zhaoxi.AdvancedCourse.Models.ItemInfo"://完整名称
Type itemType =Type.GetType(fullName);//ItemInfo的Type对象

//创建ItemInfo实例
object itemInfo=Activator.createInstance(itemType);//调用无参构造函数来动态创建
//ItemInfo item02 =(ItemInfo)objItem;

//创建ItemInfo的类型
ObjectHandle objItem2 = Activator.createInstance("zhaoxi.Advancedcourse" ,fullName);
ItemInfo item2=objItem2.Unwrap();//打开包装对象

查看类型元数据

通过反射,我们可以读取类型中的元数据信息,如属性、字段、方法等。

public class ItemInfo
{

    public int Id{get;set;}
    public string Name { get;set;}
    public string Remark { get; set;}
    private string ItemType = "Income";
    public string GetItemInfo()
    {
        return Id +","+ Name+","+Remark;
    }
 }
 
Type itemType=typeof(ItemInfo);

//1.获取类中的公有属性
PropertyInfo[] pros= itemType.GetProperties();//获取所有的公有展性
foreach(var proc in pros)
{
    Console.WriteLine(proc.Name + ":" + proc.GetValue());
}
PropertyInfo proName=itemType.GetProperty("Name")://获取指定名称的属性
//获取对象的值
ItemInfo itemInfo=new ItemInfo(){Id=101,Name="收入" ,Remark="收入名目"}
string itemName=proName.GetValue(itemInfo).Tostring();//Name属性值
proName.setValue(itemInfo,"支出");

//2.公有字段
FieldInfo[] fields=itemType.GetFields();//获取所有的公有字段
FieldInfo remark = itemType.GetField("Remark").toString();//获取指定的公有字段的名称
string remarkValue = remark.GetValue(item).ToString();//获取指定名称的字段的值

// 私有字段
FieldInfo fType =  itemType.GetField("ItemType",BindingFlags.NoPublic | BindingFlags.Instance);
string typeValue = type.GetValue(item).ToString();

//3.方法
MethodInfo[] methods =itemType.GetMethods();//所有的公共方法
//获取GetItemInfo()方法并调用
MethodInfo getInfo =itemType.GetMethod("GetItemInfo");
//调用方法
object restr=getInfo.Invoke(itemInfo,null);
//获取无参的方法
MethodInfo mGetRecordInfo =expendType.GetMethod("GetRecordInfo");
//无参方法的调用
string restr= mGetRecordInfo.Invoke(expendobj, null).Tostring();
//带一个参数的方法
MethodInfo mcalTotalExpend1 = expendType.Getmethod("calTotaExpend", new Type[] {typeof(decimal)});
//带一个参数方法调用
decimal totalExpend = 10;
totalExpend =(decimal) mCalTotalExpendl.Invoke(expendobj, new object[] { totalExpend });

//补充:泛型方法获取与调用
MethodInfo createobj =typeof(GenericMethodclass).GetMethod("createobj");
MethodInfo createobjNew=createobj.MakeGenericMethod(typeof(IncomeInfo));
IncomeInfo income=(IncomeInfo)createobjNew.Invoke(null,null);
// 构造函数获取以及通过构造函数创建对象
{
    // 获取类型,创建实例
    Type icomeType = typeof(IncomeInfo);
    //获取无参构造函数
    var cons01=incomeType.Getconstructor(new Type[]{ });
    //object incomeobj=cons01.Invoke(null);//通过无参构造所数创建实例
    //带一个参数的
    var cons02=incomeType.Getconstructor(new Type[]{ typeof(int)});
    //通过带一个int参数的构造函数创建实例
    //object incomeobj= cons02.Invoke(new object[] {id});
    //带两个参数的
    var cons03 = incomeType.Getconstructor(new Type[] { typeof(int),typeof(decimal) });
    //通过带两个参数的构造函数创建实例
    //object incomeobj=cons03.Invoke(new object[]{ id,amount });
}

动态加载程序集

Assembly ass =null;
//加载程序集方式1:不写d11文件的后缀名 dll文件放在bin/Debug/net6.0下
ass = Assembly.Load("zhaoxi.Models");
//加载方式2 写程序集文件的完整路径(绝对路径)
ass = Assembly.LoadFile(@"D:\zhaoxi.windvancedcourse\bin\Debug\zhaoxi.Models.dll");
//加载方式3 要写d11后缀名
ass = Assembly.LoadFrom("zhaoxi.Models.dll");