ASP.NET Core C# 反射 & 表达式树 (第二篇)
前言
上一篇讲到了各种反射的操作方式, 这一篇主要说说如何找到类型.
Type Information
在找类型的时候, 除了依据简单的 string 以外, 还会用到很多类型属性来做判断.
比如它是不是 value type, 它是不是 Enum, 它是不是 interface, 它是不是继承了某一个类型.
常用到的识别
// is inherit other class var yes1 = typeof(Dog).IsAssignableTo(typeof(Animal)); // true var yes2 = typeof(Animal).IsAssignableFrom(typeof(Dog)); // true var yes3 = typeof(int).IsValueType; // true var yes4 = typeof(string).IsValueType; // false var yse5 = typeof(Enum1).IsEnum; // true var yes6 = typeof(Interface1).IsInterface; // true // have generic ? var yes7 = typeof(HaveGeneric<>).IsGenericType; // true
Is nullable string?
从 .NET 6 开始, 反射可以检查出是不是 nullable string 和 reference 了.
How to use .NET reflection to check for nullable reference type
var type = typeof(Person); var context = new NullabilityInfoContext(); var nameProperty = type.GetProperty("Name")!; var nameInfo = context.Create(nameProperty); var isNullable = nameInfo.ReadState == NullabilityState.Nullable; // true
BindingFlags
BindingFlags 能在获取 properties, methods, fields, etc. 的时候, 指定 private, public, static, etc. 的范围.
比较常用到的
图片l来源: 详解 .NET 反射中的 BindingFlags 以及常用的 BindingFlags 使用方式
常用的方式
var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); var properties = type.GetProperties(); var methods = type.GetMethods(); // 不包括 constructor var fields = type.GetFields(); var members = type.GetMembers(); // 全部东西
默认就是 Public, Instance, Static
Property Info
一些常用到的 property info 判断
public class Person { public string Name1 { get; private set; } = ""; // setter is private public string Name2 { private get; set; } = ""; // getter is private public string Name3 { get; } = ""; // get only public string Name4 { get; init; } = ""; // setter is init // public string SetOnly { set; } = ""; // 报错, 不可以只有 set // private string Name3 { public get; set; } // 报错, 前面 private 后面 public 是不行的 }
1. BindingFlags.Public, 只要前面是 public 就获取得到, setter 是 private 无所谓.
2.CanWrite
typeof(Person).GetProperty("Name1")!.CanWrite
除非完全没有 set, 要不然 CanWrite 就是 true, init 依然是 true.
3. GetSetMethod 可以用来判断是不是 private, 参考: How to check if property setter is public
var t1 = typeof(Person).GetProperty("Name1")!.GetSetMethod(); // null var t2 = typeof(Person).GetProperty("Name2")!.GetSetMethod(); // ok var t3 = typeof(Person).GetProperty("Name3")!.GetSetMethod(); // null var t4 = typeof(Person).GetProperty("Name4")!.GetSetMethod(); // ok var person = Activator.CreateInstance(typeof(Person)); typeof(Person).GetProperty("Name1")!.SetValue(person, "dada"); // ok typeof(Person).GetProperty("Name2")!.SetValue(person, "dada"); // ok typeof(Person).GetProperty("Name3")!.SetValue(person, "dada"); // error, can't set typeof(Person).GetProperty("Name4")!.SetValue(person, "dada"); // ok
Generic 高级
var type = typeof(Animal<,>); var yes = type.IsGenericType; // 虽然还没有填充内容, 它依旧是一个 GenericType var yes1 = type.ContainsGenericParameters; // 表示它还没有填充内容 (虽然字面意思好像是反过来 /.\, 但总之 true 就表示还没有 MakeGenericType 就对了...) type.MakeGenericType(new Type[] { typeof(string), typeof(int) }); var yes2 = type.ContainsGenericParameters; // 依然是 true, 因为填充不是 void method var newType = type.MakeGenericType(new Type[] { typeof(string), typeof(int) }); var no = newType.ContainsGenericParameters; // false, 这就表示已经有填充内容了. var yes3 = type == typeof(Animal<,>); // yes var yes4 = newType == typeof(Animal<string,int>); // yes var yes5 = newType.GetGenericTypeDefinition() == typeof(Animal<,>); // yes 通过 GetGenericTypeDefinition 可以返回还没填充的类型
上一篇只是讲了简单的导入,导出. 这篇补上一些其它小细节.
1. MakeGenericType 不是 void method 哦
2. ContainsGenericParameters = true 表示这个 type 还没有被填充 MakeGenericType, false 表示已经有内容了.
3. IsGenericType 不管有没有内容, 只要是 Generic 就是 true
4. GetGenericTypeDefinition 返回还没有填充的 Generic Type
Class has implement some interface?
要判断一个 class 是否有 implement 某个 interface, 最好的方式就是拿这个 class type 的所有 interfaces 出来对比
/// <summary> /// check if class implement interface /// </summary> /// <param name="classType"></param> /// <param name="interfaceType">if interface is empty generic [,] then class will use empty generic too for compare, if not then == compare</param> /// <returns></returns> public static bool HasImplementInterface(Type classType, Type interfaceType) { var requireIsGenericType = interfaceType.IsGenericType; var requireIsEmptyGeneric = interfaceType.ContainsGenericParameters; // is empty <,> ? return classType.GetInterfaces().Where(classInterface => { if (requireIsGenericType && requireIsEmptyGeneric) { if (!classInterface.IsGenericType) return false; return classInterface.GetGenericTypeDefinition() == interfaceType; } else { return classInterface == interfaceType; } }).Any(); }
注: 这里是判断 Type, 如果是实例, 直接 object is Interface 就可以了, 不过那个就不是反射了.
Get Right Overload Method
当方法有重载的时候, 就不可以仅仅通过方法名来获取到正确的方法.
需要判断它的 Generic, 还有它的 Parameters 类型
public static MethodInfo GetMethod(Type classType, string methodName, List<Type> genericTypes = null!, List<Type> parameterTypes = null!) { genericTypes ??= new(); parameterTypes ??= new(); return classType.GetMethods() .Where( m => m.Name == methodName && m.GetGenericArguments().Length == genericTypes.Count && m.GetParameters().Length == parameterTypes.Count ) .Select(m => genericTypes.Count > 0 ? m.MakeGenericMethod(genericTypes.ToArray()) : m) .Single(m => m.GetParameters().Select(p => p.ParameterType).SequenceEqual(parameterTypes)); }
首先是 name, generic count, parameter count 要对
然后把 generic type 填充进去 method type, 然后才可以对比 parameters.
因为 generic 可能就会用到 parameter 上, 如果没有填充好的话, 对比 parameter type 就会错了.
Get Right Overload Constructor
public static ConstructorInfo GetConstructor(Type classType, List<Type> paramTypes = null!) { paramTypes ??= new(); return classType.GetConstructors() .Where(m => m.GetParameters().Length == paramTypes.Count) .Single(m => m.GetParameters().Select(p => p.ParameterType).SequenceEqual(paramTypes)); }
这个就比较简单, 因为 Constructor 不会有 Generic, 有 Generic 的是它的 Class
总结
到这里, 反射就算有个基本功了. 下一篇来看看表达式树.
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
2018-11-02 Asp.net core 学习笔记 (library)