C#反射和特性的使用方法
Type类型
Type. IsAssignableFrom 方法:确定指定类型的实例是否能分配给当前类型实例。
Console.WriteLine(typeof(Person).IsAssignableFrom(typeof(Student)));//True Console.WriteLine(typeof(Student).IsAssignableFrom(typeof(Person)));//False
Type. IsSubclassOf 方法:确定当前 Type 是否派生自指定的 Type。
Console.WriteLine(typeof(Person).IsSubclassOf(typeof(Student)));//False Console.WriteLine(typeof(Student).IsSubclassOf(typeof(Person)));//True
Assembly程序集
1.1反射加载程序集
//获取最初执行代码的程序集路径 System.Reflection.Assembly.GetExecutingAssembly().CodeBase //加载dll;当前程序集需存在这个名字的dll Assembly assembly = Assembly.LoadFrom("TextDll.dll");
1.2获取类型
//获取程序集的指定类型,(类型名称需要包含命名空间) Type type = assembly.GetType("TextDll.TestDemo"); //获取嵌套类型 Type type = assembly.GetType("TextDll.TestDemo+Student");
1.3创建类型的实例
//获取类型的实例 var obj = Activator.CreateInstance(type);//创建对象的实例,默认无参数构造函数 var obj2 = Activator.CreateInstance(type,new object[] { "123"});//创建对象的实例,一个字符串参数的构造方法 obj = Activator.CreateInstance(type,true);//创建对象的实例,使用私有构造方法
1.4获取实例的方法
//获取实例方法 var methodInfo = type.GetMethod("Quary");//通过名称获取类型的方法 methodInfo.Invoke(obj, null);//调用方法,静态方法可将实例为null methodInfo = type.GetMethod("Quary",BindingFlags.Instance|BindingFlags.NonPublic);//实例私有方法 //获取普通类的泛型方法 var methodInfo = type.GetMethod("Quary");//通过名称获取类型的方法 var methodGeneric = methodInfo.MakeGenericMethod(new Type[] { typeof(int), typeof(string) });//确定方法的参数类型和个数(泛型方法) methodGeneric.Invoke(obj, new object[] { 1, "Ant编程" });
1.5获取泛型类
//获取泛型类的泛型方法 Assembly assembly = Assembly.LoadFrom("TextDll.dll");//加载Dll,也可以完整路径 Type type = assembly.GetType("TextDll.TestDemo`2");//获取泛型类,类型数量2
1.6创建泛型类的实例
var typeNew = type.MakeGenericType(new Type[] { typeof(int), typeof(string) });//确定泛型类的参数类型 var obj = Activator.CreateInstance(typeNew);//创建对象的实例,默认构造方法
1.7获取泛型类的泛型方法
var methodInfo = type.GetMethod("Quary");//找到方法 var methodGeneric = methodInfo.MakeGenericMethod(new Type[] { typeof(int), typeof(string) });//确定方法的参数类型和个数(泛型方法) methodGeneric.Invoke(obj, new object[] { 1, "Ant编程" });
1.8获取属性
//获取属性 Assembly assembly = Assembly.LoadFrom("TextDll.dll");//加载Dll,也可以完整路径 Type type = assembly.GetType("TextDll.TestDemo");//获取泛型类,类型数量2 var obj = Activator.CreateInstance(type);//创建对象的实例,默认构造方法 var id = type.GetProperty("ID"); if (id != null) { id.SetValue(obj, 2); } PropertyInfo[] propertyInfos = type.GetProperties();//所有属性
设置实例的属性,考虑属性是枚举类型
var value = "First"; var propertyInfo = typeof(Student).GetProperty("MyEnum"); propertyInfo?.SetValue(student, propertyInfo.PropertyType.IsEnum ? Enum.Parse(propertyInfo.PropertyType, value) : Convert.ChangeType(value, propertyInfo.PropertyType), null);
1.9获取枚举类型的值
首先需要从内部了解一下枚举(Enumeration)
enumMyEnum { AAA, BBB, CCC } //背后的IL是这样的: .classprivate auto ansi sealed MyEnum extends [mscorlib]System.Enum { .field publicstatic literal valuetype Mgen.MyEnum AAA = int32(0) .field publicstatic literal valuetype Mgen.MyEnum BBB = int32(1) .field publicstatic literal valuetype Mgen.MyEnum CCC = int32(2) .field public specialname rtspecialname int32 value__ }
其实枚举中的常量都是静态的字段。而枚举对象的值会保存在非静态的特殊字段value__中。因此,用反射来获取名称其实就是获取类型的所有静态字段就可以了,如下代码:
var fields =typeof(MyEnum).GetFields(BindingFlags.Static |BindingFlags.Public); foreach (var fi in fields) Console.WriteLine(fi.Name);
对于值得获取也很简单,通过反射得到的代表静态字段的FieldInfo来获取值就可以,并且获取的值仍属于枚举类型的。如果想获取枚举背后的类型,仍需要Enum.GetUnderlyingType方法,如下代码:
var fields =typeof(MyEnum).GetFields(BindingFlags.Static |BindingFlags.Public); foreach (var fi in fields) { var value = fi.GetValue(null); Console.WriteLine("值:{0} 类型:{1} 枚举背后类型:{2}", value, value.GetType(), Enum.GetUnderlyingType(value.GetType())); }
2.0获取实例的委托属性并执行
var assembly = Assembly.GetExecutingAssembly(); var type = assembly.GetType("ConsoleTestUntil.Student"); var obj = Activator.CreateInstance(type); var propertyInfo = type.GetProperty("PrintName", BindingFlags.Public | BindingFlags.Instance); var value = propertyInfo.GetValue(obj); if(value is Delegate @delegate) { @delegate.DynamicInvoke(new object[] {default}); }
class Student { public string Name { get; set; } class Course { public string Name { get; set; } } public Action<object> PrintName { get { return new Action<object>(e => { Console.WriteLine("这个是委托");}); } } }
2.1获取执行静态方法
Assembly assembly = Assembly.LoadFrom(程序集路径); Type type = assembly.GetType(命名空间.类名); MethodInfo method=type.GetMethod(方法名,BindingFlags.Static | BindingFlags.Public); method.Invoke(null, null);//静态方法的实例为null
备注
获取程序集资源文件,必须 过滤掉动态程序集
.Where(p => !p.IsDynamic)
反射判断类型是否是可以枚举的方法
var parameter = new List<int>(); var asga = parameter.GetType().GetInterface(typeof(IEnumerable<>).FullName); // 遍历类型实现的所有接口,判断是否存在某个接口是泛型,且是参数中指定的原始泛型的实例。 var sgag= parameter.GetType().GetInterfaces().Any(x => typeof(IEnumerable<>) == (x.IsGenericType ? x.GetGenericTypeDefinition() : x));
反射方法创建委托
下面是官方的例子
public delegate void D1(C c, string s); public delegate void D2(string s); public delegate void D3(); public class C { private int id; public C(int id) { this.id = id; } public void M1(string s) { Console.WriteLine("Instance method M1 on C: id = {0}, s = {1}", this.id, s); } public static void M2(string s) { Console.WriteLine("Static method M2 on C: s = {0}", s); } } static void Main(string[] args) { C c1 = new C(42); // Get a MethodInfo for each method. // MethodInfo mi1 = typeof(C).GetMethod("M1", BindingFlags.Public | BindingFlags.Instance); MethodInfo mi2 = typeof(C).GetMethod("M2", BindingFlags.Public | BindingFlags.Static); D1 d1; D2 d2; D3 d3; Console.WriteLine("\nAn instance method closed over C."); // In this case, the delegate and the // method must have the same list of argument types; use // delegate type D2 with instance method M1. // //委托与方法必须由相同的参数,使用实例方法创建委托 Delegate test = Delegate.CreateDelegate(typeof(D2), c1, mi1, false); // Because false was specified for throwOnBindFailure // in the call to CreateDelegate, the variable 'test' // contains null if the method fails to bind (for // example, if mi1 happened to represent a method of // some class other than C). // if (test != null) { d2 = (D2)test; // The same instance of C is used every time the // delegate is invoked. d2("Hello, World!"); d2("Hi, Mom!"); } Console.WriteLine("\nAn open instance method."); // In this case, the delegate has one more // argument than the instance method; this argument comes // at the beginning, and represents the hidden instance // argument of the instance method. Use delegate type D1 // with instance method M1. // //委托的一个参数未实例, d1 = (D1)Delegate.CreateDelegate(typeof(D1), null, mi1); // An instance of C must be passed in each time the // delegate is invoked. // d1(c1, "Hello, World!"); d1(new C(5280), "Hi, Mom!"); Console.WriteLine("\nAn open static method."); // In this case, the delegate and the method must // have the same list of argument types; use delegate type // D2 with static method M2. // //使用静态方法的委托 d2 = (D2)Delegate.CreateDelegate(typeof(D2), null, mi2); // No instances of C are involved, because this is a static // method. // d2("Hello, World!"); d2("Hi, Mom!"); Console.WriteLine("\nA static method closed over the first argument (String)."); // The delegate must omit the first argument of the method. // A string is passed as the firstArgument parameter, and // the delegate is bound to this string. Use delegate type // D3 with static method M2. // //省略委托的第一个参数 d3 = (D3)Delegate.CreateDelegate(typeof(D3), "Hello, World!", mi2); // Each time the delegate is invoked, the same string is // used. d3(); }
创建委托
var mi1 = typeof(C).GetMethod("M1"); //委托类型,委托的第一个默认参数,方法 Action<string> actionM1= (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), null, mi1); actionM1.Invoke("");
下面是关于属性转换为委托的测试
{// 属性示例:public Action<object> Commod { get; set; } = new Action<object>(e => Console.WriteLine($"{e?.ToString()}你好")); var info = frame.GetMethod().DeclaringType.GetProperties().Where(p => p.Name == "Commod" && typeof(Action<object>).IsAssignableFrom(p.PropertyType)) .FirstOrDefault(); //使用实例属性的GetGetMethod()创建Func委托,返回值为Action<object> var proGetDel = Delegate.CreateDelegate(typeof(Func<Action<object>>), ribbonUI, info.GetGetMethod()) as Func<Action<object>>; proGetDel.Invoke().Invoke("实例属性的Get访问器进行包装:--"); //第二个参数未null,将抛异常 //(Delegate.CreateDelegate(typeof(Func<Action<object>>), null, info.GetGetMethod()) as Func<Action<object>>) //.Invoke().Invoke("GetGetMethod:--"); //直接调用实例属性的GetGetMethod()创建Func委托,返回值为Action<object> (info.GetGetMethod().Invoke(ribbonUI, null) as Action<object>) .Invoke("直接调用Get访问器:--"); } { //静态属性 public static Action<object> CommodSt { get; set; } = new Action<object>(e => Console.WriteLine($"{e?.ToString()}你好")); var infoSt = frame.GetMethod().DeclaringType.GetProperties() .Where(p => p.Name == "CommodSt" && typeof(Action<object>).IsAssignableFrom(p.PropertyType)) .FirstOrDefault(); //使用实例属性的GetGetMethod()创建Func委托,返回值为Action<object> var proGetDel = Delegate.CreateDelegate(typeof(Func<Action<object>>), null, infoSt.GetGetMethod()) as Func<Action<object>>; proGetDel.Invoke().Invoke("静态属性CommodSt的 Get访问器进行包装:--"); //直接调用静态属性的GetGetMethod()创建Func委托,返回值为Action<object> (infoSt.GetGetMethod().Invoke(null,null) as Action<object>) .Invoke("直接调用CommodSt属性的Get访问器:--"); } {//lambda属性 public Action<object> CommodLambda => e => Console.WriteLine($"{e?.ToString()}你好"); //其实就是实例方法 var info = frame.GetMethod().DeclaringType.GetProperties().Where(p => p.Name == "CommodLambda" && typeof(Action<object>).IsAssignableFrom(p.PropertyType)) .FirstOrDefault(); //使用实例属性的GetGetMethod()创建Func委托,返回值为Action<object> var proGetDel = Delegate.CreateDelegate(typeof(Func<Action<object>>), ribbonUI, info.GetGetMethod()) as Func<Action<object>>; proGetDel.Invoke().Invoke("实例属性的Get访问器进行包装:--"); //第二个参数未null,如果引用了实例的属性,将抛异常 (Delegate.CreateDelegate(typeof(Func<Action<object>>), null, info.GetGetMethod()) as Func<Action<object>>) .Invoke().Invoke("GetGetMethod:--"); //直接调用实例属性的GetGetMethod()创建Func委托,返回值为Action<object> (info.GetGetMethod().Invoke(ribbonUI, null) as Action<object>) .Invoke("直接调用Get访问器:--"); }
判断是否为静态属性
if(info.GetGetMethod().IsStatic) { }
二、特性
内置常用特性标记
[Serializable] //标记序列化
[DebuggerStepThrough] //标记调试跳过改方法 [Required] //标记值不能为空
//自定义特性的简单列子 class AttributeTest { public void Test() { Type type = typeof(UseDemo); var customAttributes = type.GetCustomAttributes(true); foreach (var item in customAttributes) { var defindAttribute = item as DefindAttribute; if (defindAttribute != null) { Console.WriteLine(defindAttribute.ShowInfo); } } } } [Defind("这是一个特性创建")] class UseDemo { public static void TestMain() { } } [AttributeUsage(AttributeTargets.Class)]//约束特性使用范围 class DefindAttribute : Attribute { public DefindAttribute(string showInfo) { ShowInfo = showInfo; } public string ShowInfo { get; set; } }
方法中获取被调用方法的调用堆栈,同时打印方法的属性标记的简单列子
class Program { static void Main(string[] args) { TestAttribute testAttribute = new TestAttribute(); testAttribute.MethodA(); Console.Read(); } } public class TestAttribute { [Remark("第一个方法")] public void MethodA() { BaseMethod(() => { Console.WriteLine("MethodA"); }); } private void BaseMethod(Action action) { var methodInfo = action.Method; StackTrace ss = new StackTrace(true); foreach (var frame in ss.GetFrames()) { var attributes = frame.GetMethod().GetCustomAttributes(); foreach (var attribute in attributes) { if(attribute is RemarkAttribute remark) Console.WriteLine(remark.Remark); } } action.Invoke(); } } public class RemarkAttribute : Attribute { public string Remark { get; set; } public RemarkAttribute(string Remark) { this.Remark = Remark; } }
给枚举添加特性标记的简单示例
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] public sealed class EnumDescriptionAttribute : Attribute { private string description; public string Description { get { return this.description; } } public EnumDescriptionAttribute(string description) : base() { this.description = description; } public static string GetDescription(Enum value) { if (value == null) throw new ArgumentNullException("value"); System.Reflection.FieldInfo fieldinfo = value.GetType().GetField(value.ToString()); var attribute = fieldinfo.GetCustomAttribute(typeof(EnumDescriptionAttribute), false); if (attribute is EnumDescriptionAttribute enumDescription) return enumDescription.Description; return string.Empty; } }
反射获取调用堆栈中的特性
/// <summary>
/// 获取调用堆栈中符合当前类型的特性 .Reverse()
/// 备注:字段与构造函数不会被获取
/// </summary>
/// <returns>null</returns>
public static IEnumerable<T> GetStackFramsCustomAttribute<T>() where T : Attribute { return new System.Diagnostics.StackTrace(true).GetFrames().Select(f => f.GetMethod()) .Where(f => f.Name != ".ctor").SelectMany(method => { return method.DeclaringType.GetInterfaces() .Select(t => t.GetMethod(method.Name)) .Where(t=>t!=null) .SelectMany(t => t.GetCustomAttributes(typeof(T), true)) .Concat(Get()); IEnumerable<object> Get() { if (method.Name.Contains("<get_")) { var name = Regex.Match(method.Name, "(?<=get_).*(?=>)")?.Value; var property = !string.IsNullOrWhiteSpace(name) ? (method.IsAssembly ? method.DeclaringType.DeclaringType : method.DeclaringType) ?.GetProperty(name, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetProperty | System.Reflection.BindingFlags.Instance) : null; return (property?.GetCustomAttributes(typeof(T), false)) ?? Enumerable.Empty<object>(); } else return method.GetCustomAttributes(typeof(T), true); } }) .Cast<T>(); }
自定义特性
/// <summary> /// 控件名称标记的属性 /// </summary> [SuppressSniffer, AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class ButtonNameRematkAttribute : Attribute { public ButtonNameRematkAttribute(object name) { Name = name; } public object Name { get; set; } }
使用示例,如果属性的写法是 public Action<object> ActionSet { get;},特性标记不会被读取到,所以修改了上面特性标记只能标记在方法上
internal class Program { static void Main(string[] args) { GetAction.Invoke(""); Console.WriteLine("Hello World!"); } [ButtonNameRematkAttribute("静态: GetAction 属性")] static Action<object> GetAction { get { return e => ActionMethod.Invoke(""); } } static Action<object> ActionMethod = e => TestMethod(); [ButtonNameRematkAttribute("静态: TestMethod 方法")] static void TestMethod() { Program program = new Program(); program.Action.Invoke(""); } [ButtonNameRematkAttribute("实例: Action 属性")] Action<object> Action { get { return e => InfoMain(); } } Action InfoMethod = () => { var s = AttributeExtensions.GetStackFramsCustomAttribute<ButtonNameRematkAttribute>(); foreach (var item in s) Console.WriteLine(item.Name); }; [ButtonNameRematkAttribute("实例:InfoMain 方法")] void InfoMain() { InfoMethod.Invoke(); } public Program() { } }
输出结果:
官方详细参考链接:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/attributes/
特性的创建和使用参考 https://www.cnblogs.com/chenxizhaolu/p/9497768.html