c#入门-反射

反射

这是一个RPG游戏可能用到的类型。
里面定义了角色的属性,有生命,魔法,攻击,防御,速度。
重写了ToString方法,可以在转为字符串时输出全部的属性。

class Hero
{
	public float hp, maxHp, mp, maxMp, atk, def, speed;
	
	public override string ToString()
		=> $"hp:{hp},maxHp:{maxHp},mp:{mp},maxMp:{maxHp},atk:{atk},def:{def},speed:{speed}";
}

看着就很不雅观吧。
手动填写全部的属性和名字,不能跟随变化。
如果属性非常多,有几十个,写起来非常头疼。
然后如果某一天发现,一个属性没有用,想删掉,那就很麻烦了。

为了能让他动态地跟随我们定义的字段输出内容,我们可以使用反射。
反射是在程序运行中,访问数据的元数据的能力。
(元数据是描述数据的数据。比如hp=12。12就是数据,hp这个字段的名字就是元数据。)

获取元数据

获取元数据和六个类有关(需要引入System.Reflection命名空间):
程序集Assembly类:用于批量获得Type实例。
类型Type类:用于获得一个类型的成员。
属性PropertyInfo类
方法MethodBase类
字段FieldInfo 类
事件EventInfo 类

从实例上获取Type实例

任何类型都具有GetType方法,因为这是object上的方法,所有类型都继承于它。

Type t="hello".GetType();

注:反射会直接检查运行时的实际内容。也就是说检查变量装的值而不是变量的类型。
也就是说,此法不能获取接口类型。因为接口必然通过类来实现,接口实例无法单独存在。

从类型上获取Type实例

使用关键字表达式typeof()获取类型的Type。
这里的类型可以是泛型占位符。

Type GetType<T>()
{
	return typeof(T);
}

从字符串获取Type实例

使用Type的静态方法GetType加上类型的完全限定名可以获取。

Type type = Type.GetType("System.Int32");

获得当前(执行的代码)程序集

当前程序集使用Assembly类的静态方法GetExecutingAssembly获取

Assembly ass = Assembly.GetExecutingAssembly();

获取类成员

4个类成员类都需要通过Type实例来获取。

查看元数据信息

需要的信息可能各种各样,这里就不解释为了解决什么问题了,直接用代码和注释说明。
以下只展示各种类型的实例有什么属性和方法,不能运行。

程序集

using System.Reflection;

Assembly ass = Assembly.GetExecutingAssembly();
string name = ass.FullName;//输出程序集全名
Type[] type = ass.GetTypes();//获取这个程序集下所有Type实例

类型

类型本身的信息:

Type type = typeof(string);
Assembly ass = type.Assembly;//获得这个类所在的程序集
Type Base = type.BaseType;//获取这个类的直接基类
string name = type.Name;//获得这个类的名字
string fullName = type.FullName;//获得这个类的完全限定名

#region 判断类型
bool b1 = type.IsArray;//这个类型是数组吗
bool b2 = type.IsClass;//这个类型是类吗
bool b3 = type.IsEnum;//这个类型是枚举吗
bool b4 = type.IsInterface;//这个类型是接口吗
bool b5 = type.IsValueType;//这个类型是值类型吗
#endregion

#region 判断修饰符
bool b6 = type.IsSealed;//这个类型是密封的吗
bool b7 = type.IsAbstract;//这个类型是抽象的吗(抽象类或接口)
bool b8 = type.IsPublic;//是public的吗
bool b9 = type.IsNested;//是嵌套类吗
#endregion

获取类成员:

Type type = typeof(string);

#region 获得全部成员
FieldInfo[] v1 = type.GetFields(BindingFlags.Public | BindingFlags.Instance);        //获得所有公共实例字段
EventInfo[] v2 = type.GetEvents(BindingFlags.Public | BindingFlags.Instance);       //获得所有公共实例事件
MethodInfo[] v3 = type.GetMethods(BindingFlags.Public | BindingFlags.Instance);     //获得所有公共实例方法
PropertyInfo[] v4 = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);//获得所有公共实例属性
ConstructorInfo[] v5 = typeof(string).GetConstructors(BindingFlags.Instance | BindingFlags.Public);//获得所有公共实例构造器
Type[] v6 = type.GetInterfaces();//获得所有实现的接口Type
#endregion

#region 用指定名字搜索成员
FieldInfo v7 = type.GetField("Length", BindingFlags.Public | BindingFlags.Instance);       //获得指定公共实例字段
EventInfo v8 = type.GetEvent("Length", BindingFlags.Public | BindingFlags.Instance);       //获得指定公共实例事件
MethodInfo v9 = type.GetMethod("Length", BindingFlags.Public | BindingFlags.Instance);     //获得指定公共实例方法
PropertyInfo v10 = type.GetProperty("Length", BindingFlags.Public | BindingFlags.Instance); //获得指定公共实例属性
ConstructorInfo v11 = typeof(string).GetConstructor(BindingFlags.Instance | BindingFlags.Public, null);//获得公共无参构造器
Type v12 = type.GetInterface("Length");//获得所有实现的接口Type
#endregion

BindingFlags是按位枚举,可以指定搜索的条件,例如实例,静态,继承,公开,私有。
如果不使用BindingFlags参数,则默认只搜索公开成员。
如果找不到指定条件的成员,那么返回值为长度为0的数组,或null。

获得全部的成员,使用的单词是对应的所搜指定成员单词的复数形式。
大部分都是直接加s,而像属性这种以y结尾的单词,改复数需要把y改成ies

字段

FieldInfo filed = typeof(string).GetField("Length");
string name = filed.Name;//获得字段的名字
Type DeclaringType = filed.DeclaringType;//获得字段所处的类Type
Type filedType = filed.FieldType;//获得字段本身的Type

bool b1 = filed.IsInitOnly;//是否是只读的
bool b2 = filed.IsLiteral;//是否是常量
bool b3 = filed.IsPublic;//是否是公开的
bool b4 = filed.IsPrivate;//是否是私有的 
bool b5 = filed.IsStatic;//是否是静态的

属性

PropertyInfo property = typeof(string).GetProperty("Length");
string name = property.Name;//获得属性的名字
Type DeclaringType = property.DeclaringType;//获得属性所处的类Type
Type filedType = property.PropertyType;//获得属性本身的Type

bool b1 = property.CanRead;//是否有get访问器
bool b2 = property.CanWrite;//是否有set访问器

方法

MethodInfo method = typeof(string).GetMethod("Length");
string name = method.Name;//获得属性的名字
Type DeclaringType = method.DeclaringType;//获得方法所处的类Type
Type filedType = method.ReturnType;//获得方法返回值的Type

bool b1 = method.IsPublic;//是否是公开的
bool b2 = method.IsPrivate;//是否是私有的
bool b3 = method.IsStatic;//是否是静态的
bool b4 = method.IsVirtual;//是否是虚拟的

有关方法的参数信息:

ParameterInfo[] par = method.GetParameters();//获得参数列表信息
string par1 = par[0].Name;//参数名称;
Type par2 = par[0].ParameterType;//参数类型
int par3 = par[0].Position;//形参位置
object par4 = par[0].RawDefaultValue;//可选参数默认值

bool par5 = par[0].IsIn;//是否是in传递参数
bool par6 = par[0].IsOut;//是否是out传递参数
bool par7 = par[0].IsRetval;//是否是ref传递参数

使用元数据

获得了Type类型后,可以对此类型的实例进行操作。
但是,反射可以越过访问权限,破坏了c#本来的设计意图。
并且,反射本身就很消耗资源,尽量少用。

反射一个很大的用处是为了控制程序发布以后的插件。
插件制作的时候,你的程序已经发布出去了,所以制作当时是无法获得插件的内容的。
但是如果你真的打算给别人插件的权限,那应该要求别人继承你的类,以多态形式使用。

反射无法预测类型,所有参数和返回值都是object类型。

获取和修改字段

string s = "hello";
Type type = s.GetType();
var field = type.GetField("Length");
var Length = field.GetValue(s);//获得字段
field.SetValue(s, 100);//修改字段

调用对象的方法

因为方法可以重载,如果想找到指定的方法,则必须填入参数列表。
参数列表使用Type数组传递。

using System.Reflection;

string s = "hello";
Type type = s.GetType();
var method = type.GetMethod("Length", BindingFlags.Instance, new Type[] { typeof(int) });
method.Invoke(s, new object[] { 12 });//从s上调用找到的方法,传进去的参数数组作为调用方法的实参

var con = type.GetConstructor(BindingFlags.Public, new Type[] { });
var obj = con.Invoke(new object[] { });//调用构造器实例化对象
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值