反射和特性(详细)
反射
C#
复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace _03_反射
{
internal class Program
{
static void Main(string[] args)
{
// System.Reflection 命名空间中的类与 System.Type 使你能够获取有关加载的程序集和其中定义的类型的信息,如类、接口和值类型(即结构和枚举)。
// 可以使用反射在运行时创建、调用和访问类型实例。
// 反射就是提供了一种可以动态访问程序集成员的一种方案
// ----------------反射可以让我们动态的添加程序
// 使用反射动态的加载程序
Assembly assembly = Assembly.LoadFile(@"C:\Users\g8496\Desktop\11-6\01 作业\bin\Debug\01 作业.exe");
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{
Console.WriteLine(type.FullName);
}
// ---------------反射可以根据类型读取该类型中的信息
// System.Type 一般上作为我们使用反射的入口,大部分反射的操作都要使用 System.Type来完成
// Type 是一个类,存储某个类型的详细信息,也就是元数据(metadata),可以用它获取到该类的属性和方法
Type t1 = typeof(string); // 使用 typeof(类型) 获取该类型的详细信息
Type t2 = typeof(int);
Type t3 = typeof(Dog);
Dog dog = new Dog(); // 根据现有对象获取类型的详细信息
Type t4 = dog.GetType();
// ------------------使用Type获取某个类的所有属性
// 1、根据对应的类型生成Type对象
Type d = typeof(Dog);
// 2、调用方法获取属性
PropertyInfo[] infos = d.GetProperties();
// 3、循环所有的属性
foreach (PropertyInfo info in infos)
{
Console.WriteLine(info.Name); // 属性的名字
}
Console.ReadLine();
}
}
}
动态加载DLL
C#
复制代码
using ClassLibrary;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace _04_动态加载DLL
{
internal class Program
{
static void Main(string[] args)
{
// 我们可以使用反射来动态的读取dll文件中的元数据,包括这个dll中有哪些类、属性、方法
// Load 只写dll的文件名,但是要保证当前项目的运行目录中有该dll文件
// Assembly as1 = Assembly.Load("ClassLibrary");
// LoadFile 加载指定路径下的dll文件,需要写绝对路径(从盘符(C: D:)开始的路径)
// Assembly as2 = Assembly.LoadFile(@"C:\Users\g8496\Desktop\11-6\04 动态加载DLL\bin\Debug\ClassLibrary.dll");
// LoadFrom 加载指定路径下的dll文件,支持绝对路径和相对路径(注意:要带文件后缀)
Assembly as3 = Assembly.LoadFrom(@"ClassLibrary.dll");
// Assembly 加载了对应的dll后,其中存储了该程序中所有的类型
// GetTypes() 获取该dll/exe中所有的类型
Type[] ts = as3.GetTypes();
foreach (Type v in ts)
{
// Type.FullName 获取该类型的完全限定名(带有命名空间的类名)
// Type.Name 获取该类型的名字(类名)
Console.WriteLine($"v.FullName: {v.FullName} Name:{v.Name}");
}
// Type至关重要,它是对程序元数据访问和操作的入口
Type t = typeof(People); // 根据类获取类型
Console.WriteLine(t.FullName);
Type t1 = new People().GetType(); // 根据对象获取类型
Console.WriteLine(t1.FullName);
// 封装方法输出完全限定名
WriteFullName<string>();
WriteFullName<Dog>();
Console.ReadLine();
}
// 输出类的完全限定名
static void WriteFullName<T>()
{
Type t = typeof(T);
Console.WriteLine(t.FullName);
}
}
}
反射操作
C#
复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using ClassLibrary;
namespace _05_反射操作
{
internal class Program
{
static void Main(string[] args)
{
Assembly assembly = Assembly.LoadFrom("ClassLibrary.dll");
// GetTypes 获取dll中所有的类型
Type[] types = assembly.GetTypes();
// GetType 获取程序中指定的类型,参数为对应类型的完全限定名(带命名空间的)
Type t = assembly.GetType("ClassLibrary.User");
// 使用 Type.GetProperties 获取该类型的所有属性
PropertyInfo[] infos = t.GetProperties();
foreach (PropertyInfo info in infos)
{
Console.WriteLine("属性:" + info.Name + "类型:" + info.PropertyType);
}
// 使用 Type.GetMethods 获取该类型的所有方法
// 包含属性的get、set,以及类自己的方法,还有继承自父类的方法
MethodInfo[] methods = t.GetMethods();
foreach (MethodInfo method in methods)
{
Console.WriteLine("方法" + method.Name);
}
Console.WriteLine("----------------------------------");
// 使用反射实例化类
{
// 根据某个类型信息生成实例(new操作),执行无参构造
object o = Activator.CreateInstance(t);
}
{
// 执行有参构造方法创建对象
object o1 = Activator.CreateInstance(t, new object[] { "1","张三","155555",11});
object o2 = Activator.CreateInstance(t, new object[] { "张三", 18 });
}
{
// 获取类型的所有构造方法(了解)
ConstructorInfo[] ctors = t.GetConstructors(); // 获取所有构造方法
foreach (ConstructorInfo ctor in ctors)
{
Console.WriteLine(ctor.Name); // 构造方法的名字都是 .ctor
ParameterInfo[] parameters = ctor.GetParameters(); // 获取当前构造方法的参数列表
Console.WriteLine(parameters.Length); // 当前构造方法的参数个数
Console.Write("所需参数:");
foreach (ParameterInfo v in parameters) // 循环参数列表
{
// v.Name 参数名
// v.ParameterType 参数类型
Console.WriteLine(v.Name + ":" + v.ParameterType);
}
Console.WriteLine();
}
}
Console.WriteLine("------------------------");
{
// 使用反射,即使该类的访问修饰符为:internal,照样可以通过反射访问它并实例化
// new Dog(); 报错,因此Dog的修饰符为internal
Type d = assembly.GetType("ClassLibrary.Dog");
object o = Activator.CreateInstance(d);
// new ClassLibrary.Cai.Qiu.Kun(); 报错,因为构造方法是私有的
Type d1 = assembly.GetType("ClassLibrary.Cai.Qiu.Kun");
object o1 = Activator.CreateInstance(d1, true); // 参数2给一个true,表示该构造方法可能为非公开(非 public)的
}
Console.ReadLine();
}
}
}
使用反射创建泛型类
C#
复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using ClassLibrary;
namespace _06_使用反射创建泛型类
{
internal class Program
{
static void Main(string[] args)
{
//new TestTClass<int, string, bool>(1, "测试", true);
//new TestTClass<int, double, char>(1, 2.2, 'c');
// 如何使用反射创建泛型类
// 1、加载程序
Assembly assembly = Assembly.LoadFrom("ClassLibrary.dll");
// 2、获取对应的类型(注意:如果该类型拥有泛型参数,需要以 `数字 结尾,数字取决于泛型的个数)
Type t = assembly.GetType("ClassLibrary.TestTClass`3");
// 3、带有泛型参数的类型其实是一个不完整的类型,因此带有泛型参数的类型不能直接使用,需要指定对应的泛型参数
// 根据泛型类型生成新的类型
// 注意:传入的应该是一个 Type 类型,使用 typeof 生成
Type newT = t.MakeGenericType(typeof(int), typeof(string), typeof(bool));
// newT == TestTClass<int, string, bool>
// 4、进行实例化
object o = Activator.CreateInstance(newT, new object[] {123,"张三",false});
Console.ReadLine();
}
}
}
使用反射调用方法
C#
复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using TestLibrary;
namespace _02_使用反射调用方法
{
internal class Program
{
static void Main(string[] args)
{
// 1、加载程序
Assembly assembly = Assembly.LoadFrom("TestLibrary.dll");
// 2、获取对应的类型
Type peopleType = assembly.GetType("TestLibrary.People");
// 3、创建实例化对象
object o1 = Activator.CreateInstance(peopleType, new object[] { "张三", 20 });
object o2 = Activator.CreateInstance(peopleType, new object[] { "李四", 21 });
Console.WriteLine("----------------调用无参的方法---------------");
{
// 不适用委托调用方法:
// People p1 = new People();
// p1.Say();
// 使用反射调用方法
// 1、获取该类型中对应名字的方法
MethodInfo method = peopleType.GetMethod("Eat");
// 2、调用方法,
// 参数1:指定该方法在哪个实例上运行
// 参数2:调用方法传递的参数,如果没有参数可以传null
method.Invoke(o1, null); // 我张三会吃饭
method.Invoke(o2, null); // 我李四会吃饭
}
Console.WriteLine("---------------调用有参构造-------------------");
{
MethodInfo method = peopleType.GetMethod("Play");
method.Invoke(o1, new object[] { "穿越火线" });
method.Invoke(o1, new object[] { "元神启动" });
method.Invoke(o2, new object[] { "PUBG" });
}
Console.WriteLine("---------------调用重载方法-------------------");
{
// 如果一个方法拥有多个重载,那么在获取该方法时就必须手动指定方法的类型
MethodInfo method = peopleType.GetMethod("Say", new Type[] { });
method.Invoke(o1, null);
MethodInfo method1 = peopleType.GetMethod("Say", new Type[] { typeof(string) });
method1.Invoke(o1, new object[] { "小红叫我去网吧,我置之不理" });
MethodInfo method2 = peopleType.GetMethod("Say", new Type[] { typeof(string), typeof(string) });
method2.Invoke(o1, new object[] { "今天上网吧打通宵", "小明" });
}
Console.WriteLine("---------------调用私有方法-------------------");
{
// 调用私有方法
MethodInfo method = peopleType.GetMethod("Test", BindingFlags.Instance | BindingFlags.NonPublic);
method.Invoke(o2, null);
// 调用私有的重载方法
// Type.GetMethod(方法名, BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[]{ 参数类型 }, null)
MethodInfo method1 = peopleType.GetMethod("Test1", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(string) }, null);
method1.Invoke(o2, new object[] {"测试"});
MethodInfo method2 = peopleType.GetMethod("Test1", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(string), typeof(int) }, null);
method2.Invoke(o2, new object[] { "测试222", 999 });
}
// 重要:GetMethod Invoke
Console.ReadLine();
}
}
}
属性操作
C#
复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using TestLibrary;
namespace _03_属性操作
{
internal class Program
{
static void Main(string[] args)
{
// 1、加载程序
Assembly assembly = Assembly.LoadFrom("TestLibrary.dll");
// 2、获取类型
Type peopleType = assembly.GetType("TestLibrary.People");
// 创建实例
object o = Activator.CreateInstance(peopleType);
// 1、获取类型中所有的属性
PropertyInfo[] infos = peopleType.GetProperties();
foreach (PropertyInfo pi in infos)
{
Console.WriteLine($"属性名:{pi.Name} 类型:{pi.PropertyType}");
}
{
// 2、使用反射设置对象的属性
// 2-1、先获取对应的属性
PropertyInfo nameInfo = peopleType.GetProperty("Name");
// 2-2、设置对应对象的属性值
// 参数1:设置到哪个对象上
// 参数2:设置的属性值
nameInfo.SetValue(o, "张三");
// 设置对象o的Age属性为22
PropertyInfo a = peopleType.GetProperty("Age");
a.SetValue(o, 22);
}
{
// 3、使用反射读取对象的属性
// 3-1、获取对应的属性
PropertyInfo nameInfo = peopleType.GetProperty("Name");
// 3-2、读取对应对象的属性值
Console.WriteLine("o对象的Name属性值为:" + nameInfo.GetValue(o));
}
Console.WriteLine("============");
// 在控制台输出该参数的所有属性名、属性值、属性类型
People p1 = new People();
Test.Fn(p1);
Test.Fn(new People() { Name = "万物", Age = 30 });
Console.ReadLine();
}
}
class Test
{
public static void Fn<T>(T v)
{
// 1、获取该类型的Type
Type t = typeof(T);
// Type t = v.GetType(); // 或者
// 2、获取该Type的所有属性
PropertyInfo[] properties = t.GetProperties();
// 3、循环所有属性
foreach (PropertyInfo pi in properties) {
// 输出属性名,属性类型,属性值
Console.WriteLine($"{pi.Name}: {pi.PropertyType} = {pi.GetValue(v)}");
}
}
}
}
特性
C#
复制代码
// #define abc // 定义一个预处理标识 abc
// 一般预处理标识仅在当前文件中生效,如果需要该标识在整个项目中生效,需要在项目的Properties
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _05_特性
{
internal class Program
{
static void Main(string[] args)
{
// 什么是特性
// **特性(Attribute)**是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。
// 特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。
// .Net 框架提供了两种类型的特性:预定义特性(内置特性)和自定义特性。
// 特性的语法如下
// 特性(Attribute)的名称和值是在方括号内规定的,放置在它所应用的元素之前。positional_parameters 规定必需的信息,name_parameter 规定可选的信息。
// [attribute(必须的信息1, 必须的信息2, 可选的信息 = value, 可选的信息 = value)]
// 元素
// 特性就是给C#中的元素(类、属性、字段、方法...)添加一些其他的信息
// 预定义特性(内置特性)
Test test = new Test();
test.Fn();
test.Fn1();
test.Fn2();
test.Fn3();
for (int i = 0; i < 100; i++)
{
test.TestMethodSpeed();
}
new Class1().Fn();
Console.WriteLine("Obsolete特性");
test.Fn4();
test.Fn5();
// test.Fn7();
Console.ReadLine();
}
}
class Test
{
// Conditional(名字) 该特性标记了一个条件,如果执行编译时定义了该预处理标识符,这个属性或者方法才会被定义和调用
[Conditional("abc")]
public void Fn()
{
Console.WriteLine("我是Fn方法,abc标识被定义时执行");
}
[Conditional("abc")]
public void Fn1()
{
Console.WriteLine("我是Fn1方法,abc标识被定义时执行");
}
[Conditional("DEBUG")]
public void Fn2()
{
Console.WriteLine("我是Fn2,DEBUG标识被定义时执行");
}
[Conditional("aaa")]
public void Fn3()
{
Console.WriteLine("我是Fn3,aaa标识被定义时执行");
}
// 测试方法的速度
[Conditional("DEBUG")]
public void TestMethodSpeed()
{
Console.WriteLine("假装测试方法的执行速度,DEBUG标识被定义时执行");
}
[Obsolete("这个方法不让用了")]
public void Fn4() { }
[Obsolete("Fn5已经过时,请你使用Fn6方法")]
public void Fn5() { }
public void Fn6() { }
// 参数2传递一个布尔值,表示如果使用该方法是是否展示一个错误而不是警告
[Obsolete("提示信息", true)]
public void Fn7() { }
// 同时使用多个特性
[Conditional("DEBUG")]
[Obsolete("过时了")]
public void Fn8()
{
}
// 同时使用多个特性的另一种写法
[Conditional("DEBUG"), Obsolete("过时了")]
public void Fn9()
{
}
}
}
自定义特性
C#
复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _06_自定义特性
{
// 自定义特性就是一个类,需要继承子Attribute
// 一般我们自定义的特性都以Attribute结尾,在使用该特性时可以省略Attribute
// 创建了一个特性,该特性用于记录某个元素的bug修复情况
public class BugFixAttribute : Attribute
{
// bug修复者
public string Name { get; set; }
// bug修复时间
public string Time { get; set; }
// bug的描述信息
public string Description { get; set; }
public BugFixAttribute(string name, string time)
{
Name = name;
Time = time;
}
}
public class LaoguoAttribute: Attribute {
public int A { get; set; }
public string B { get; set; }
public LaoguoAttribute(int a)
{
A = a;
}
}
// 有一个内置的特性用于规定我们的特性可以用在什么地方
// 默认特性可以用在所有的地方
// Method 方法
// Property 属性
// Constructor 构造函数
// Field 字段
// Class 类
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Class )]
public class ABCAttribute : Attribute
{
}
}
//------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _06_自定义特性
{
internal class Program
{
static void Main(string[] args)
{
}
}
// [ABCAttribute]
[ABC] // 简化
// 构造方法中要求的参数直接写,非构造方法要求的参数 xxxx = xxxx 的格式书写
[BugFix("老王", "2023-10-21", Description = "修复了一个不执行的bug")]
class Test
{
// 19行这种写法类似于类实例化时
// new BugFixAttribute("老王", "2023-10-21")
// {
// Description = "修复了一个不执行的bug"
// }
}
[BugFix("老李", "2023-11-23")]
[Laoguo(1, B = "B的值")]
class People
{
}
[ABC]
class A
{
[ABC]
private int x;
[ABC]
public int MyProperty { get; set; }
[ABC]
public void Fn() { }
[ABC]
public A()
{
}
}
}
特性的应用
C#
复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace _02_特性
{
internal class Program
{
static void Main(string[] args)
{
Type t = typeof(Peolpe);
// 反射的作用就是用来读取类型上的一些元数据
Console.WriteLine(t.Name); // 类名
Console.WriteLine(t.FullName); // 完全限定名
Console.WriteLine(t.Namespace); // 命名空间
// 特性就是给C#的元素添加了一些其他的信息(元数据)
// GetCustomAttributes 获取元素上所有的特性
// 参数:如果只传递一个Bool值,表示是否要获取该类继承自别的类的特性
var b1 = typeof(A).GetCustomAttributes(true); // 1个特性
var b2 = typeof(A).GetCustomAttributes(false); // 0个特性
// 另一个重载,用于筛选我们要获取该元素上的哪些特性
// 参数1:你要获取哪个类型的特性
// 参数2:传递一个Bool值,表示是否要获取该类继承自别的类的特性
var b3 = t.GetCustomAttributes(typeof(EfgAttribute), false);
var b4 = t.GetCustomAttributes(typeof(BugFixAttribute), false);
// 获取某一个特性
Attribute attribute = t.GetCustomAttribute(typeof(BugFixAttribute));
Console.WriteLine("t这个类型拥有特性BigFix,修改该bug的人是:" + (attribute as BugFixAttribute).Name);
// 练习:获取People类型上的属性bug修复情况
// 1、先获取所有的属性
foreach (PropertyInfo p in t.GetProperties())
{
// 2、获取该属性的 BugFixAttribute 特性
Attribute att = p.GetCustomAttribute(typeof(BugFixAttribute));
// 3、如果有该特性,则输出即可
if(att != null)
{
Console.WriteLine($"{p.Name}属性被修复,修改人:{(att as BugFixAttribute).Name}");
}
}
Console.ReadKey();
}
}
// 使用该特性
[BugFix("张三")]
[Efg]
[Efg]
[Efg]
class Peolpe
{
[BugFix("王五")]
public int MyProperty1 { get; set; }
[BugFix("王五")]
public int MyProperty2 { get; set; }
public int MyProperty3 { get; set; }
}
// 创建了一个自定义特性
class BugFixAttribute : Attribute
{
public string Name { get; set; }
public BugFixAttribute(string name)
{
Name = name;
}
}
// Inherited 如果为true,则使用了该特性的类的子类也会继承该特性(如下例中 A 类同样会拥有 B 类的特性)
[AttributeUsage(AttributeTargets.All, Inherited = true)]
class AbcAttribute : Attribute { }
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
class EfgAttribute : AbcAttribute { }
[Abc]
class B { }
class A : B { }
}
//===================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace _03_特性使用练习
{
internal class Program
{
static void Main(string[] args)
{
Test.BugLog<People>();
Console.ReadLine();
}
}
[BugFix("张三", "2023-10-22", "测试一下")]
class People
{
[BugFix("张三", "2023-10-22", "测试一下")]
public int MyProperty { get; set; }
public int MyProperty1 { get; set; }
[BugFix("张三", "2023-10-22", "测试一下")]
public void Fn()
{
}
public void Fn1()
{
}
}
class Test
{
public static void BugLog<T>()
{
// 输出参数T所包含的所有bugFix特性的信息
// 1、根据参数生成 Type
Type t = typeof(T);
// 2、先看这个类有没有该特性
WriteInfo(t, "类");
// 如:xxxx方法: xx年xx月xx日xxx修复,bug描述:xxxxxx
// 4、循环方法
foreach (MethodInfo v in t.GetMethods())
{
WriteInfo(v, "方法");
}
// 如:xxxx属性: xx年xx月xx日xxx修复,bug描述:xxxxxx
foreach(PropertyInfo v in t.GetProperties())
{
WriteInfo(v, "属性");
}
}
private static void WriteInfo(MemberInfo t, string text)
{
Attribute attr = t.GetCustomAttribute(typeof(BugFixAttribute));
BugFixAttribute attr2 = (BugFixAttribute)attr;
// 3、判断并输出
if (attr != null)
{
Console.WriteLine($"{t.Name}{text}: {attr2.Time}{attr2.Name}修复,bug描述:{attr2.Message}");
}
}
}
class BugFixAttribute : Attribute
{
public string Name { get; set; }
public string Time { get; set; }
public string Message { get; set; }
public BugFixAttribute(string name, string time, string message)
{
Name = name;
Time = time;
Message = message;
}
}
}