反射、
关于反射
在 C# 中,反射是一种机制,它允许在程序运行时获取类型的信息并操作对象。通过反射,我们可以在运行时动态创建对象、访问对象的属性和方法、获取类型信息等。反射可以帮助我们实现很多功能,如动态创建对象、解析和调用程序集中的方法和属性、动态加载和卸载程序集等。反射是一个强大的机制,但使用不当也可能会对性能产生影响,所以在使用反射时需要慎重考虑。
给一个对象,在不用new操作符的情况下,在不知道具体类型的情况下,创建一个同类型的对象,还能访问这个对象的各个成员。
——↓ 引自《C# 7.0核心技术指南》——————————————————
3.3.3 GetType方法和typeof运算符C#中的所有类型在运行时都会维护System.Type类的实例。有两个基本方法可以获得System.Type对象:
· 在类型实例上调用GetType方法
· 在类型名称上使用typeof运算符
GetType在运行时计算,而typeof在编译时静态计算(如果是用泛型类型参数,那么它将由即时编译器解析)。
System.Type拥有诸多属性,例如类型的名称、程序集、基类型等属性。
例如:
using System;
public class Point { public int X, Y; }
class Test
{
static void Main()
{
Point p = new Point();
Console.WriteLine (p.GetType().Name); // Point
Console.WriteLine (typeof (Point).Name); // Point
Console.WriteLine (p.GetType() == typeof(Point)); // True
Console.WriteLine (p.X.GetType().Name); // Int32
Console.WriteLine (p.Y.GetType().FullName); // System.Int32
}
}
——↑ 引自《C# 7.0核心技术指南》——————————————————
视频学习:【103-反射之Type】 B站:IT萌叔Jack
//获取值类型的Type类对象
Type type01=12.GetType();
Type type02 = typeof(System.Int32);
Type type03 = Type.GetType("System.Int32");
//判断三个Type类对象是否相同:True
Console.WriteLine(type01 == type02);
Console.WriteLine(type02 == type03);
//获取引用类型的Type类对象
Type type04 = new object().GetType();
Type type05 = typeof(System.Object);
Type type06 = Type.GetType("System.Object");
Console.WriteLine(type04 == type05);
Console.WriteLine(type05 == type06);
视频学习:【104-反射之Field】B站:IT萌叔Jack
class Person
{
//字段
public string name = "jack";
public int age = 30;
public static string nation;
}
class Program
{
static void Main(string[] args)
{
//通过反射获取Person类型信息
Type type = typeof(反射与依赖注入.Person);
//获取字段:获取所有public的字段
// public FieldInfo[ ] GetFields(); 返回值是FieldInfo[ ]
FieldInfo[ ] fieldInfos= type.GetFields();
//遍历
for (int i = 0; i < fieldInfos.Length; i++)
{
//Console.WriteLine($"字段类型:{fieldInfos[i].FieldType} 字段名称:{fieldInfos[i].Name} 字段值:{fieldInfos[i].GetValue}");
//非静态字段,需要通过类的对象去访问字段 才能拿到值。
Console.WriteLine($"字段类型:{fieldInfos[i].FieldType} 字段名称:{fieldInfos[i].Name} 字段值:{fieldInfos[i].GetValue(new Person() )}");
}
}
}
输出:
字段类型:System.String 字段名称:name 字段值:jack
字段类型:System.Int32 字段名称:age 字段值:30
字段类型:System.String 字段名称:nation 字段值:
nation 是引用类型,没有初始化值,默认字段值是null
private int id;
//想要访问这个私有字段, 可以通过 标记
进入 “FieldInfo[ ] fieldInfos= type.GetFields();” 的GetFields方法。
这是它的一些重载
public FieldInfo? GetField(string name);
public abstract FieldInfo? GetField(string name, BindingFlags bindingAttr);
public FieldInfo[] GetFields();
public abstract FieldInfo[] GetFields(BindingFlags bindingAttr);
BindingFlags
public enum BindingFlags
{
······
Instance = 4,
Static = 8,
NonPublic = 32,
······
}
FieldInfo[ ] fieldInfos= type.GetFields(BindingFlags.Instance| BindingFlags.NonPublic);
用 | 竖线添加多个
class Person
{
//字段
private int id = 1001;
public string name = "jack";
public int age = 30;
public static string nation;
}
class Program
{
static void Main(string[] args)
{
//通过反射获取Person类型信息
Type type = typeof(反射与依赖注入.Person);
// BindingFlags.Instance 指定获取的是 Instance 实例字段, 实例字段是非静态的字段 创建之后才能获得的字段
FieldInfo[] fieldInfos = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
// 去获取 public ,static的字段
FieldInfo[] fieldInfos02 = type.GetFields(BindingFlags.Static | BindingFlags.Public);
//遍历
for (int i = 0; i < fieldInfos.Length; i++)
{
Console.WriteLine($"字段类型:{fieldInfos[i].FieldType} 字段名称:{fieldInfos[i].Name} 字段值:{fieldInfos[i].GetValue(new Person() )}");
}
//获取指定的字段
FieldInfo field = type.GetField("nation");
//设置值:静态字段 不需要对象, 因此传递null
field.SetValue(null,"中国");
//获取值 field.GetValue() 静态的不需要实例,不用传,但是传了也没事,
//动态的需要实例 需要传 new Person()
Console.WriteLine(field.GetValue(null));
}
}
简易的访问私有字段和属性:
using System;
using System.Reflection;
public class Example
{
private int id = 10;
private void Method()
{
Console.WriteLine("私有方法被执行了");
}
public static void Main(string[] args)
{
var example = new Example();
// 获取Example对象的类型
Type type = example.GetType();
// 获取id字段并设置可访问性
FieldInfo fieldInfo = type.GetField("id", BindingFlags.NonPublic | BindingFlags.Instance);
fieldInfo.SetValue(example, 20);
// 获取Method方法并设置可访问性
MethodInfo methodInfo = type.GetMethod("Method", BindingFlags.NonPublic | BindingFlags.Instance);
methodInfo.Invoke(example, null);
}
}
在这个例子中,我们使用var example = new Example()创建了Example类的实例,并传递给GetType()方法来获取该对象的类型。接着,我们分别通过GetField()和GetMethod()方法获取了需要访问的私有字段和方法的信息,并通过SetValue()和Invoke()方法进行了访问。
需要注意的是,默认情况下,私有字段和方法是不可访问的。因此,我们需要在调用GetField()和GetMethod()方法时,通过参数设置来指定其可访问性。具体来说,在这里我们使用了BindingFlags.NonPublic和BindingFlags.Instance来表示访问非公有成员和实例成员。
另外,C# 还提供了一种dynamic类型,可以在运行时动态解析类型信息。因此,在某些情况下,也可以使用 dynamic来访问私有字段和方法:
using System;
public class Example
{
private int id = 10;
private void Method()
{
Console.WriteLine("私有方法被执行了");
}
public static void Main(string[] args)
{
dynamic example = new Example();
// 访问私有字段
example.id = 20;
Console.WriteLine(example.id); // 输出:20
// 访问私有方法
example.Method(); // 输出:私有方法被执行了
}
}
视频学习:【105-反射之Property 属性】B站:IT萌叔Jack
class Person
{
//字段
private int id = 1001;
private string name = "jack";
private int age = 30;
private static string nation;
//属性
public int Id { get => id; set => id = value; }
public string Name { get => name; set => name = value; }
public int Age { get => age; set => age = value; }
public static string Nation { get => nation; set => nation = value; }
}
class Program
{
static void Main(string[] args)
{
Type type = new Person().GetType();
//获取共有的所有属性
PropertyInfo [ ] propertyInfos= type.GetProperties();
//遍历
foreach (PropertyInfo property in propertyInfos)
{
Console.WriteLine($"属性类型:{property.PropertyType} 属性名:{property.Name} 属性值:{property.GetValue(new Person())}");
}
//获取指定属性
PropertyInfo info = type.GetProperty("Nation");
//设置值
info.SetValue( null,"波兰" );
// 获取值
Console.WriteLine(info.GetValue(null));
}
}
构造函数里面不能直接使用静态字段, 静态字段要初始化的话,必须要把它都放到静态构造函数中
public object Invoke(object?[]? parameters);
这是C#中的Constructor.Invoke方法,它用于调用一个对象的构造函数,可以通过传递参数进行初始化。该方法返回一个Object类型的实例,表示新创建的对象。
方法的参数是一个可空的object[]类型,其中包含要传递给构造函数的参数列表。如果该构造函数不需要传递任何参数,则可以传递null参数或使用一个元素数为0的数组。
在调用Constructor.Invoke方法时,可能会遇到NotSupportedException异常,因为某些类型(如System.TypedReference, System.ArgIterator和System.RuntimeArgumentHandle)不能使用Invoke方法来创建实例。此外,还可能会出现SecurityException异常,这是由于调用者没有足够的代码访问权限造成的。
需要注意的是,Invoke方法只能用于创建具有公共访问级别的构造函数创建的对象。如果要创建私有构造函数创建的对象,则需要改用Activator.CreateInstance方法,该方法可以指定创建私有构造函数对象的访问级别。
class Person
{
//字段
private int id = 1001;
private string name = "jack";
private int age = 30;
private static string nation;
//属性
public int Id { get => id; set => id = value; }
public string Name { get => name; set => name = value; }
public int Age { get => age; set => age = value; }
public static string Nation { get => nation; set => nation = value; }
//构造函数
public Person()
{
}
public Person(int id, string name, int age)
{
this.id = id;
this.name = name;
this.age = age;
}
static Person()
{
nation = "新加坡";
}
//重写ToString ()
public override string ToString()
{
return $" Person类:[id={this.id} name={this.name} age={this.age}]";
}
}
class Program
{
static void Main(string[] args)
{
Type type = Type.GetType("反射与依赖注入.Person");
//获取公有的构造函数
//ConstructorInfo[ ] constructorInfos= type.GetConstructors(); //取得公有的 非静态的构造函数
//获取静态 非公有的 构造函数
ConstructorInfo[] constructorInfos = type.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic);
foreach (ConstructorInfo constructor in constructorInfos)
{
Console.WriteLine($"构造函数名:{constructor.Name} 构造函数参数个数:{ constructor.GetParameters().Length} 是否静态:{constructor.IsStatic} ");
}
// 获取指定的构造函数
ConstructorInfo constructorInfo = type.GetConstructor(new Type[] { typeof(int),typeof(string),typeof(int) });
//创建对象
//constructorInfo.Invoke(new object[] { 1002,"lucy",28}); //把原来的默认值更改了
//Invoke创建对象,并返回此对象 但是返回的是object
Person person= constructorInfo.Invoke(new object[] { 1002, "lucy", 28 }) as Person;
Console.WriteLine(person.ToString()); // ToString 默认的是输出 父类的全名,
// 如果想要通过ToString输出 这类的字段的值,就重写ToString
//ToString()方法是Object类的一个虚拟方法
//获取静态构造函数
ConstructorInfo cons= type.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic)[0];
cons.Invoke(null);
//获取
Console.WriteLine(Person.Nation);
}
}
我这里有个报错 和两个警告
cons.Invoke(null);
System.MemberAccessException异常,错误消息为“Type initializer was not callable.
不过这样写 还是报错。
Assembly.Load("ReflectionTest");
Type type = Type.GetType("ReflectionTest.Person, ReflectionTest"); // full namespace and class name
这是一个关于 .NET Core 目标框架不匹配的警告提醒。在您的 ReflectionTest
项目中,你使用了 Microsoft.Extensions.DependencyInjection.Abstractions
版本为7.0.0 的 NuGet 包。
该警告指出,此版本 NuGet 包不支持.NET Core3.1,并且没有经过测试。如果您想解决此问题,应该升级您的项目的目标框架为 .NET Core6.0 或更高版本,以保证包的兼容性。
要解决此问题,可以考虑以下两种方法之一:
1.升级项目的 .NET Core 目标框架版本到6.0或更高版本。
2.在项目文件中设置 <SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
,以忽略这个警告并尝试在不支持的配置下运行。
请注意,如果您选择第二种方法,则会存在 execution-time 错误的风险,因为您的项目使用的 NuGet 包与其目标框架不兼容。
建议您优先考虑升级到最新的 .NET Core 版本和相关 NuGet 包版本来确保最大程度上的代码兼容性和维护性。
[视频学习:【107-反射之Method】B站:IT萌叔Jack ] (https://www.bilibili.com/video/BV1rg4y1t7QX/?share_source=copy_web&vd_source=deaf604b58cb5a6dbd34d697e9efa138)
BindingFlags.DeclaredOnly是一个枚举值,在C#中被用于反射机制中。它表示只绑定已声明的成员,不包括继承自基类或实现接口的成员。
使用BindingFlags.DeclaredOnly标志后,反射的获取成员信息的范围将被限制为当前类型所声明的成员
例如,如果一个类A继承自另外一个类B,并且实现了一个接口I,那么如果使用BindingFlags.DeclaredOnly标志进行反射时,只能获取A类本身定义的成员,而不能获取从B类和I接口中继承或实现的成员。这种方式通常被用于一些特定场景,比如只想获取当前类中显式声明的成员信息,而不需要获取继承自父类的同名成员信息,以此提高程序搜索性能。
class Person
{
private string name;
private int id;
private static string nation;
public static string Nation { get => nation; set => nation = value; }
public string Name { get => name; set => name = value; }
public int Id { get => id; set => id = value; }
public Person()
{
nation = "新加坡";
}
public Person(int id, string name)
{
this.id = id;
this.name = name;
}
public string Inroduce()
{
return $"我是{this.Name} 编号{this.Id} ";
}
//静态带参函数
public static void ShowNation(string nation)
{
Nation = nation;
Console.WriteLine($"国籍:{Nation}");
}
}
class Program
{
static void Main(string[] args)
{
Type type = typeof(ReflectionTest.Person);
//获取公有函数
//MethodInfo[] methodInfos = type.GetMethods();
//获取private函数 DeclaredOnly 自己声明的
MethodInfo[] methodInfos = type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public);
//遍历
foreach (MethodInfo methodInfo in methodInfos)
{
Console.WriteLine($"函数返回值:{ methodInfo.ReturnType} 函数名:{ methodInfo.Name} 函数参数长度:{methodInfo.GetParameters().Length}");
}
//获取指定的函数
//MethodInfo method= type.GetMethod("Introduce",null);
//method.Invoke(new Person(1003,"peter"), null);
//Console.WriteLine( method.Invoke(new Person(1003,"peter"), null) );
MethodInfo method = type.GetMethod("Introduce", new Type[] { });
Console.WriteLine(method.Invoke(new Person(1003, "peter"), new object[] { }));
//获取静态函数
MethodInfo info = type.GetMethod("ShowNation", new Type[] { typeof(string)});
info.Invoke(null,new object[] { "中国"} );
}
}
C#语言入门详解 刘铁猛 接口隔离,反射,特性,依赖注入
CSDN:鲜鱼汤是我了
C#里的反射,特性和依赖注入(接口隔离)利用实例代码来解释说明