前言
在上一章中介绍了什么是反射:
https://www.cnblogs.com/aoximin/p/16440966.html
正文
上一节讲述反射的基本原理和为什么要用反射,还用反射的优缺点这些。
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/3e103cc3551c63f1aa67685b9ba0f4ef.png)
其二者的本质是一致的,都是先获取到type(元数据)然后在进行创建实例。
下面那个好理解看下上面那个吧。
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/e03552117a3c4ead5735fe28617e704f.png)
其实还是调用了activator:
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/5da1d68e00b92b3087331b2bb064fdef.png)
说另外一个故事,是先有对象,后执行构造方法。还是先执行构造方法后有对象呢?到底是编译行为还是运行行为呢?
其实先创建对象,然后再进行构造函数。
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/54950e5dac9e8b6239dfa23809366d30.png)
而一切初始化其实是在构造函数中。
比如:
public class Cat
{
private string a = "100";
public string b = "100";
public Cat()
{
a = "200";
b = "200";
}
}
那么其.ctor () 为:
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/81714e5b4d71819e8acefc3acd56cde7.png)
计算机远比我们想象的要简单的多,就是开辟一块空间,然后往里面填充数据。 至于定义什么类型,那属于程序的自我管理。
有点扯远了,那么反射的实现也是一样的。
在CreateInstance中:
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/ece0df299ececaf6301aab83df47cea1.png)
两者创建对象的原理基本是一致的,反射只是在上面增加了一层动态获取类型(其中包括校验和创建实例的代码生成)。
玩一个有趣的东西,既然我们说了,其实创建对象其实不一定要构造函数的。且我们上面知道了RuntimeTypeHandle 可以创建对象,那就直接搞事情。
static void Main(string[] args)
{
Type catType = typeof(Cat);
Type handleType = Type.GetType("System.RuntimeTypeHandle");
var obj = Activator.CreateInstance(handleType);
var bind = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
var hiMethod = handleType.GetMethod("Allocate", bind);
var cat= hiMethod.Invoke(obj, new object[]{catType});
Console.ReadKey();
}
得到的结果为:
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/d52910c45b51747773f4046985116c5d.png)
好吧,接着往下看:
internal class Program
{
static void Main(string[] args)
{
var type1 = typeof(Cat);
Cat cat = new Cat();
var type2 =cat.GetType();
Assembly assembly = Assembly.GetExecutingAssembly();
var type3 = assembly.GetType("ConsoleApp1.Cat");
var type4 = typeof(Cat);
Console.WriteLine($"{type1.GetHashCode()} {type1.GetHashCode()} {type3.GetHashCode()} {type4.GetHashCode()}");
Console.ReadKey();
}
static (string name, int age, uint height) GetStudentInfo1()
{
return ("Bob", 28, 175);
}
}
他们的type也是同一个type:
internal class Program
{
static void Main(string[] args)
{
var type1 = typeof(Cat);
Cat cat = new Cat();
var type2 =cat.GetType();
Assembly assembly = Assembly.GetExecutingAssembly();
var type3 = assembly.GetType("ConsoleApp1.Cat");
var type4 = typeof(Cat);
Console.WriteLine($"{type1.GetHashCode()} {type2.GetHashCode()} {type3.GetHashCode()} {type4.GetHashCode()}");
Console.ReadKey();
}
static (string name, int age, uint height) GetStudentInfo1()
{
return ("Bob", 28, 175);
}
}
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/0604e4fd71ca291ddd6d6c5fb8e4ae56.png)
值得注意的是typeof 属于语法糖:
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/496645bcae87acd61ad097fad1946459.png)
现在知道了Type 就包含我们类的元数据了,那么这些元数据到底有哪些呢?
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/07c2dffe44a84f07d78a4e2fc60f85a0.png)
里面包含了描述类的全部信息,有命名空间啊,属性啊,方法啊。这些都是有的。
这些不用去记,用的时候找找看,都有的。
唯一说一个值得的注意的地方哈。
是这样的。有一个BindingFlags这个枚举,可以看到是是一个多选枚举。
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/c76545badaa205e611512a6c6ebf7aa0.png)
然后这样写:
static void Main(string[] args)
{
var type1 = typeof(Cat);
var filter = BindingFlags.Public;
var members = type1.GetMembers(filter);
Console.ReadKey();
}
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/abf1033b70d86048214365aad0cd4caf.png)
你发现看不到,这是为什么呢?
static void Main(string[] args)
{
var type1 = typeof(Cat);
var members = type1.GetMembers();
Console.ReadKey();
}
来看下是什么样的。
可以看到,这个public string b,其实是public | instance 这样的bindingflag,而不是public。
也就是说默认的是公共且可实例化的。 这个bindingflag的处理,不是或的关系,而是且的关系。
这里也是大家使用多选枚举值得注意的地方,我们的业务上不仅可以用来做或也可以用来做且,它是多选的意思。
如果需要看下反射方法是怎么调用的,可以去查看:System.RuntimeMethodHandle的InvokeMethod,这里面水比较深,选择性观看。
知道这个有什么用呢? 因为在执行invoke的时候会经过很多判断,如果是为了增加性能,可以直接调用System.RuntimeMethodHandle的InvokeMethod,一般不需要,只是说下优化手段。
例子
反射的常用手段,主要是一些例子。
获取某个namespace 下面的type:
static void Main(string[] args)
{
Assembly assembly = Assembly.GetExecutingAssembly();
var types = assembly.GetTypes().Where(u => u.Namespace == "ConsoleApp1");
Console.ReadKey();
}
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/abebb8863c3499053c3ae1319cd8b09c.png)
这个比较常用,一般来说会先加载出types,得到一个集合,然后进行管理。
有一个初学者容易犯的错误,就是认为:
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/131af868f887a9d2b192647fcf67cd3e.png)
认为这种是属性,其实这个英文是property,是特性的意思,这在有些中文书上直接说成属性,这是错误的。
然后:
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/fe21f56c66b47b18ccf27d6862a03c70.png)
这种是attribute。
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/2caf908f2f8fd901a52992e5470b1e01.png)
上面这种是字段,叫做field。
然后全部的这些,叫做member,都是成员:
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/d6d84ee7295f740c763de3a804bf3348.png)
这些概念是要区分开的。
第三个小栗子,如果获取静态的值:
static void Main(string[] args)
{
Type type = typeof(Cat);
var binding = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
var a= type.GetProperties(binding);
Cat.Test = "123456";
foreach (var item in a)
{
var test= item.GetValue(null);
Console.WriteLine($"test:{test}");
}
Console.ReadKey();
}
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/2d217a76345d9a7d40d071515ecef48d.png)
上面要说明的是GetValue,对于静态可以填空。
我们讲述到前面的构造函数,都是默认的构造函数。
static void Main(string[] args)
{
Type type = typeof(Cat);
var cat = Activator.CreateInstance(type);
Console.ReadKey();
}
这在一般情况下,很难适应,一般我们的构造函数在写一些复杂一点的时候,都会传入参数的。
public Cat(string a, string b)
{
this.a = a;
this.b = b;
}
这样的。那么怎么实例化呢?
static void Main(string[] args)
{
Type type = typeof(Cat);
var constructorInfo = type.GetConstructor(new Type[]{ typeof(string), typeof(string) });
var cat =constructorInfo.Invoke(new object[] { "123", "456"});
Console.ReadKey();
}
![重学c#系列—— 反射深入一点点[三十三]-小白菜博客](https://blog.xiaobaicai.fun/wp-content/uploads/2022/12/7ee33a52a7d840a2daf14d246bb0dd85.png)
其他的方法也是这样。
结
上一节讲述了反射,这一节讲了一下反射的大致的行为和一些简单的例子。下一节可能是io流相关的,以前写过Java的,其实两者都一样,考虑是否重新整合一下。或者直接开篇异步篇。