1.通过反射的方法,获取成员,并调用成员
1.把想要反射调用的内容存放到object中,如:
class Student { public int Id { get; set; } public int Age; public void Show() { Console.WriteLine("this is show"); } public string Show2(string name) { return $"{name}"; } } static void Main(string[] args) { //把Student类放到 object中 用反射调用的内容 object obj = new Student(); }
2.重点:使用object类中的Type类|;返回当前实例的准确运行类型,而不是对象
Type type = obj.GetType();
3.Type类常用属性
//Type常用属性 FullName完全限定名 Console.WriteLine(type.Namespace);//获取命名空间 Console.WriteLine(type.FullName);//获取该类型的完全限定名称--命名空间+类名 Console.WriteLine(type.Name);//获取当前成员的名称--类名
4.Type常用API:
获取引用类的字段:type.GetFields();type.GetField()
获取引用类的属性:type.GetProperties();type.GetPropertie()
获取引用类的方法:type.GetMethod().Invoke();type.GetMethods().Invoke()
注意:只能获取公共字段;还有获取接口,获取所有方法,就不在细说了,有兴趣的可以自己了解;
4.1获取引用类的字段:获取所有公共字段type.GetFields();获取特定字段:type.GetField()
注意:当字段属性不一致时,不能使用foreach循环赋值;
//----------------------------循环获取所有公共字段 : type.GetFields() foreach (var s in type.GetFields()) { //循环找到,再了解信息 //即获取字段的名字 if (s.Name == "Age") { Console.WriteLine(s.Name); Console.WriteLine(s.FieldType.FullName); s.SetValue(obj, 12); Console.WriteLine(s.GetValue(obj)); //在获取值或取值时,就用到上面说的type是一个,而s只知道是学生类型的字段,并不知道是哪个对象的字段,因此需要加上对象 //简单的说就是获取特定字段用 type.GetField().*** type还是一个,用的时候不知道对象是谁,因此需要传对象。这样在去理解循环遍历就好理解了:s可与理解问type.GetField } } //-----------------------------获取特定字段 直接给 GetFiled()赋值字段名------------- type.GetField("Age").SetValue(obj, 30);//取出并赋值 Console.WriteLine(type.GetField("Age").GetValue(obj));
4.2获取属性type.GetProperties()和获取字段一样
4.3 详细内容看标题2
获取引用类的方法有点特别type.GetMethod(),需要用到 Invoke(object,object[]参数列表)使用指定参数调用由当前实例表示的方法或实例,看代码
type.GetMethod("Show").Invoke(obj,new object[0]); object str= type.GetMethod("Show2").Invoke(obj, new object[] { "王五"}); Console.WriteLine(str);
完整代码:
static void Main(string[] args) { //反射: // 1.通过反射的方法,可以遍历成员,并调用成员 //把Student类放到 object中 用反射调用的内容 object obj = new Student(); //Type是个类 Type type = obj.GetType();//返回当前实例的准确运行类型 注意是类型,不是对象,后面用 //Type常用属性 FullName完全限定名 Console.WriteLine(type.Namespace); Console.WriteLine(type.FullName);//获取该类型的完全限定名称--命名空间+类名 Console.WriteLine(type.Name);//获取当前成员的名称--类名 #region 字段获取 GetFields(),GetField() //----------------------------循环获取所有公共字段 : type.GetFields() foreach (var s in type.GetFields()) { //循环找到,再了解信息 //即获取字段的名字 if (s.Name == "Age") { Console.WriteLine(s.Name); Console.WriteLine(s.FieldType.FullName); s.SetValue(obj, 12); Console.WriteLine(s.GetValue(obj));//在获取值或取值时,就用到上面说的s只知道是学生类型的字段,并不知道是哪个对象的字段,因此需要加上对象 } } //-----------------------------获取特定字段 直接给 GetFiled()赋值字段名 type.GetField("Age").SetValue(obj, 30);//取出并赋值 Console.WriteLine(type.GetField("Age").GetValue(obj)); #endregion //获取属性 Console.WriteLine("--------------------------属性-------------------"); foreach (var s in type.GetProperties()) { s.SetValue(obj, 1); Console.WriteLine(s.GetValue(obj)); } type.GetProperty("Id").SetValue(obj, 2); Console.WriteLine(type.GetProperty("Id").GetValue(obj)); type.GetMethods();//获取所有公共方法 //Invoke(object,object[]参数列表)使用指定参数调用由当前实例表示的方法或实例 type.GetMethod("Show").Invoke(obj, new object[0]); object str = type.GetMethod("Show2").Invoke(obj, new object[] { "王五" }); Console.WriteLine(str); type.GetMembers(); Console.ReadKey(); } } class Student { public int Id { get; set; } public int Age; public void Show() { Console.WriteLine("this is show"); } public string Show2(string name) { return $"{name}"; } }
2.反射调用不同参数的方法,返回结果有何不同
在后面的学习中发现了一个有趣的地方,当方法的参数不同时,返回的结果有不同,对此我做了总结,可以分为以下几种:
1.方法没有参数,且输出语句没有用到类中字段或属性;
答:正常输
2.方法没有传参,且输出语句用到类中字段或属性,且字段和属性没有赋值;已经赋值;
答:使用没有赋值,就默认初始值;使用赋值就是输出此值
3.方法传参数,但是此参数不属于字段或属性;
答:不影响,输出所传内容;
4.方法传参数,此参数属于类中字段或属性,已经赋值,没有赋值;
答:一切按照所传参数进行输出
class Student { public int Id { get; set; } public int Age; public string Name { get; set; } //1.方法没有参数,且输出语句没有用到类中字段或属性; public void Show() { Console.WriteLine("this is show"); } //2.不传参数,直接使用属性或字段 public void Show2() { Console.WriteLine("我是:" + Id); } //3.传一个就不是字段又不是属性的参数 public void Show3(string fullname) { Console.WriteLine("我是:" + fullname); } //4.传一个属于字段或属性的字段 public void Show5(int Age) { Console.WriteLine("我是:" + Age); } //在name属性复赋值前后,都调用 public void Show6(string name) { Console.WriteLine("我是:" + name); } /--------------------输出----------------- type.GetMethod("Show").Invoke(obj, new object[0]);//this is show type.GetMethod("Show2").Invoke(obj, new object[0]);//我是:0 type.GetProperty("Id").SetValue(obj, 2); type.GetMethod("Show2").Invoke(obj, new object[0]);//我是:2 type.GetMethod("Show3").Invoke(obj, new object[] { "fullname" }); //我是:fullname type.GetMethod("Show5").Invoke(obj, new object[] { 18 });//我是:18 type.GetField("Age").SetValue(obj, 12); type.GetMethod("Show5").Invoke(obj, new object[] { 18 });//我是:18 type.GetMethod("Show6").Invoke(obj, new object[] { "张三" }); //我是:张三 type.GetProperty("Name").SetValue(obj, "李四"); type.GetMethod("Show6").Invoke(obj, new object[] { "王五" }); //我是:王五
3.通过反射更新功能
动态引用一个dell 文件类库(普通dell文件,不是专门封装成方法的dell文件:比如放一个类在里面)
public class Class1 { public int Age { get; set; } public string Name { get; set; } public void Test() { Console.WriteLine($"你好我是{Name},今年{Age}岁了"); }
前提操作:
1.生成类库;2.打开类库所在文件夹,找到dell文件;3.复制到目标项目中4.点击复制到目标项目中的dell文件,属性,复制到输出目录改如果较新则更新;5.记得打开显示全部文件;6.刷新;
2.通过代码程序集Assembly 引用一下dell文件
//引用一个dell问价 类库 //一个程序集 加载指定路径上的程序集文件的内容 Assembly ass = Assembly.LoadFile(Environment.CurrentDirectory + @"\libs\ClassLibrary1.dll");
3.此时就可以对引入的dell文件判断操作了
foreach (var type in ass.GetTypes()) { Console.WriteLine(type.Name); Console.WriteLine(type.FullName); //判断是否含有***,不存在返回flase;通过一系列的is属性进行过滤 Console.WriteLine(type.IsClass); Console.WriteLine(type.IsEnum); } //.First返回序列表的第一个元素 var classs = ass.GetTypes().First(x => !x.IsEnum && x.IsClass); Console.WriteLine(classs.FullName); //注意:这里的user没有对象,但能不能生成一个呢 //如何创建 // CreateInstance 从程序集中查找指定的类型,然后使用系统激活器创建它的实例 object obj= ass.CreateInstance(classs.FullName);//相当于把类又放到了object中 //下面的就和之前的一样了 classs.GetProperty("Age").SetValue(obj,12); classs.GetProperty("Name").SetValue(obj,"李四"); classs.GetMethod("Test").Invoke(obj, null);//注意 Console.WriteLine(classs.GetProperty("Name").GetValue(obj)); Console.WriteLine(classs.GetProperty("Age").GetValue(obj)); Console.WriteLine(classs.GetMethod("Test").Invoke(obj, null));
4.更新类库
public class Class1 { public int Age { get; set; } public string Name { get; set; } public void Test2() { Console.WriteLine("我是更新类库"); } public void Test() { Console.WriteLine($"你好我是{Name},今年{Age}岁了"); Test2(); } }
,4.1将更改的类库重新生成,并替换掉目项目原来的dell,此时代码不动,结果如下
可以理解为 Test是一个按钮,Test2是增加了一个功能;