wordpress查询置顶文章/企业站seo案例分析

wordpress查询置顶文章,企业站seo案例分析,移动网站建设初学视频教程,建筑公司注册资金最低多少总目录 前言 反射是.NET框架中一个强大的特性,允许程序在运行时检查和操作类型信息。通过反射,开发者可以动态地创建对象、调用方法、访问属性等,为程序提供了极大的灵活性。本文将详细讲解C#反射的使用方法及其应用场景。 一、什么是反射&a…

总目录


前言

反射是.NET框架中一个强大的特性,允许程序在运行时检查和操作类型信息。通过反射,开发者可以动态地创建对象、调用方法、访问属性等,为程序提供了极大的灵活性。本文将详细讲解C#反射的使用方法及其应用场景。


一、什么是反射?

1. 定义

  • 反射(Reflection) 是指程序在运行时能够检查和操作其自身的类型信息。通过反射,可以获取类型的成员(如方法、属性、事件等)并动态地调用它们。
  • 在.NET框架中,反射的主要实现位于System.Reflection命名空间中。在程序运行时通过 System.Reflection 命名空间提供的 API,可完成如下操作:
    • 获取类型信息:如类名、命名空间、继承关系、实现的接口等。
    • 动态创建对象:无需显式实例化类型。
    • 访问成员:包括私有字段、方法、属性等。
    • 执行方法:通过方法名或参数动态调用。

反射,简单来说,就是程序在运行时能够自我检查,获取类型、方法、字段等元数据信息的能力。

2. 作用

  • 动态类型操作:在运行时获取类型信息(元数据),并根据这些信息执行相应的操作(动态创建对象实例或执行方法)。
  • 实现插件系统:通过反射加载外部程序集,实现可扩展的插件架构。
  • 序列化/反序列化:获取对象的字段和属性,实现数据的序列化和反序列化。
  • 自定义特性处理:读取类型或成员上的自定义特性,根据特性进行特定处理。
  • 代码分析工具:开发调试器或分析工具时,反射可以帮助你获取程序内部的状态。

3. 反射相关类与命名空间

反射的核心功能依赖于 System.Reflection 命名空间中的类型,其中包含 TypeAssemblyMethodInfo 等关键类。

1)核心类与命名空间

  • System.Reflection :反射的核心功能
    • Assembly:表示程序集,用于加载和操作程序集中的模块、类型等信息。
    • MethodInfo/PropertyInfo/FieldInfo:分别对应方法、属性、字段的元数据,支持动态调用和访问。
  • System.Type:代表任意类型(如类、接口、枚举等),可通过typeof获取类型信息。

2)相关类与命名空间

基本列举反射实际应用时所涉及的所有类与命名空间

System.Reflection(命名空间)
|
├── Assembly          	// 程序集操作
├── Module            	// 模块信息
├── ConstructorInfo   	// 构造函数
├── ParameterInfo 		// 参数信息
├── MethodInfo        	// 方法信息
├── PropertyInfo      	// 属性信息
├── MemberInfo			// 成员信息
├── FieldInfo			// 字段信息
├── TypeInfo			// 类型信息
└── MethodBase         	// 方法基类信息
|
System(命名空间)
|
├── Type              	// 类型元数据
├── Activator           // 实例创建
└── AppDomain           // 应用程序域管理

反射常用类与方法速查表

类/方法用途示例代码
Type获取类型元数据typeof(MyClass)
Assembly加载和操作程序集Assembly.Load("MyAssembly")
MethodInfo获取和调用方法method.Invoke(obj, args)
PropertyInfo访问属性值property.GetValue(obj)
BindingFlags控制成员可见性(如私有)BindingFlags.NonPublic
Activator.CreateInstance动态创建对象实例Activator.CreateInstance(type)

4. 简单示例

动态创建对象并调用方法

// 获取类型
Type type = typeof(MyClass);
// 动态创建实例
object instance = Activator.CreateInstance(type);
// 获取方法并调用
MethodInfo method = type.GetMethod("SayHello");
method.Invoke(instance, null); // 输出 "Hello, World!"
Type type = typeof(string); // 获取类型信息
object obj = Activator.CreateInstance(type); // 动态创建实例

二、如何使用反射

0. 反射操作流程概览

获取类型信息
动态创建对象
动态的访问和操作成员
动态的调用方法
动态的访问字段
动态的访问属性
动态的访问其他成员

1. 获取类型信息

Type对象是反射的基础,提供了关于类型的详细信息。

1)获取Type对象

要使用反射,首先需要获取类型的Type对象。在C#中,可以通过多种方式获取类型信息。

// 方式1:typeof运算符(编译时已知类型)
Type type1 = typeof(StringBuilder);// 方式2:GetType()(运行时对象实例)
object obj = new List<int>();
Type type2 = obj.GetType();// 方式3:Type.GetType()(通过类型名称)
Type type3 = Type.GetType("System.String");

关于获取Type对象的方式,详细信息可见:C# 获取Type对象的方式。

2)获取 Type 类中的基本属性

▶ 获取类型的基本信息
  • Name: 类型的简单名称。
  • FullName: 类型的完全限定名称(包含命名空间)。
  • Namespace: 类型所在命名空间的名称。
  • AssemblyQualifiedName: 获取类型的程序集限定名,包含类型及其所在程序集的详细信息,适用于通过反射加载类型。
namespace ReflectionDemo
{public class User { }internal class Program{static void Main(string[] args){var type = typeof(User);Console.WriteLine($"{"Name".PadRight(24)}{type.Name}");Console.WriteLine($"{"FullName".PadRight(24)}{type.FullName}");Console.WriteLine($"{"Namespace".PadRight(24)}{type.Namespace}");Console.WriteLine($"{"AssemblyQualifiedName".PadRight(24)}{type.AssemblyQualifiedName}");}}
}

运行结果:

Name                    :User
FullName                :ReflectionDemo.User
Namespace               :ReflectionDemo
AssemblyQualifiedName   :ReflectionDemo.User, ReflectionDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

关于以上属性,详情内容可见:C# Type类中Name、FullName、Namespace、AssemblyQualifiedName的区别。

Assembly 属性

通过Type对象的Assembly 属性 获取 类型所在的程序集。

using System.Reflection;namespace ReflectionDemo
{public class User { }internal class Program{static void Main(string[] args){var type = typeof(User);Assembly assembly = type.Assembly;Console.WriteLine($"Assembly FullName:{assembly.FullName}");Console.WriteLine($"Assembly Name:{type.Name}");}}
}

输出结果:

Assembly FullName:ReflectionDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Assembly Name:User

在此我们可知:


【类型的程序集限定名】= 【类型的完全限定名FullName】+【程序集的完全限定名FullName】

当我们通过Type对象的Assembly 属性 获取 类型所在的程序集之后,我们可以对程序集进行相关的操作,具体关于Assembly的相关内容将在下文进行详细介绍。

▶ 其他基础属性
namespace ReflectionDemo
{public class User { }internal class Program{static void Main(string[] args){var type = typeof(User);Console.WriteLine("基类:" + type.BaseType);      		// 输出:System.ObjectConsole.WriteLine($"IsAbstract:{type.IsAbstract}");	// 是否是抽象Console.WriteLine($"IsAbstract:{type.IsInterface}");	// 是否是接口Console.WriteLine($"IsAbstract:{type.IsClass}");		// 是否是类Console.WriteLine($"IsAbstract:{type.IsEnum}");		// 是否是枚举类型Console.WriteLine($"IsAbstract:{type.IsGenericType}");	// 是否是泛型Console.WriteLine($"IsAbstract:{type.IsPublic}");		// 是否PublicConsole.WriteLine($"IsAbstract:{type.IsSealed}");		// 是否SealedConsole.WriteLine($"IsAbstract:{type.IsValueType}");	// 是否值类型}}
}

3)获取类型的成员信息

反射不仅可以获取类型信息,还可以获取类型的成员信息,如字段、属性、方法等。

class Person
{public string Name { get; set; }private int age;
}
▶ 获取字段信息
class Program
{static void Main(){Type personType = typeof(Person);FieldInfo[] fields = personType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);foreach (var field in fields){Console.WriteLine($"Field: {field.Name}, Type: {field.FieldType}");}}
}
▶ 获取属性信息
class Program
{static void Main(){Type personType = typeof(Person);PropertyInfo[] properties = personType.GetProperties();foreach (var property in properties){Console.WriteLine($"Property: {property.Name}, Type: {property.PropertyType}");}}
}
▶ 获取方法信息
class Calculator
{public int Add(int a, int b) => a + b;private int Subtract(int a, int b) => a - b;
}class Program
{static void Main(){Type calculatorType = typeof(Calculator);MethodInfo[] methods = calculatorType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);foreach (var method in methods){Console.WriteLine($"Method: {method.Name}, Return Type: {method.ReturnType}");}}
}
  • 更多关于Type类 的详情介绍,可见:C# Type 类使用详解。
  • 更多关于BindingFlags 的详细内容,可见:C# BindingFlags 使用详解

2. 动态创建对象

反射不仅限于获取类型和成员信息,还可以用于动态创建对象。

Person类 为例:

class Person
{public int Age { get; set; }public string Name { get; set; }public Person(){Console.WriteLine("无参构造函数被执行!");}public Person(string name){Name = name;Console.WriteLine($"有参构造函数被执行,Name = {Name}");}public Person(string name, int age){Name = name;Age = age;Console.WriteLine($"有参构造函数被执行,Name = {Name}、Age = {Age}");}public void Show(){Console.WriteLine("Person");}
}

1)使用 Activator.CreateInstance 创建对象

▶ 无参构造
internal class Program
{static void Main(string[] args){Type personType = typeof(Person);object personInstance = Activator.CreateInstance(personType);var person = (Person)personInstance;person.Show();}
}

运行结果:

无参构造函数被执行!
Person
▶ 带参数构造
  • 单参数构造函数
internal class Program
{static void Main(string[] args){Type personType = typeof(Person);object[] parameters = { "Bob" };object personInstance = Activator.CreateInstance(personType, parameters);var person = personInstance as Person;person?.Show();}
}

运行结果:

有参构造函数被执行,Name = Bob
Person
  • 多参数构造函数
internal class Program
{static void Main(string[] args){Type personType = typeof(Person);object[] parameters = { "Bob", 12 };object personInstance = Activator.CreateInstance(personType, parameters);var person = (Person)personInstance;person.Show();}
}

运行结果:

有参构造函数被执行,Name = Bob、Age = 12
Person
▶ 动态创建泛型实例
Type openType = typeof(List<>);
Type closedType = openType.MakeGenericType(typeof(int));
object list = Activator.CreateInstance(closedType);
Type openType = typeof(Dictionary<,>);
Type closedType = openType.MakeGenericType(typeof(int), typeof(string));
object dict = Activator.CreateInstance(closedType);
▶ 注意事项
  • 使用Activator.CreateInstance创建具有参数化构造函数对象实例的时候,需要保持传入的参数一致
  • public Person(string name) 构造函数,需要的参数是 object[] parameters = { "Bob" };
  • public Person(string name, int age) 构造函数,需要的参数是 object[] parameters = { "Bob", 12 };

2)使用 Assembly 中的CreateInstance 创建对象

▶ 无参构造
internal class Program
{static void Main(string[] args){Assembly assembly = Assembly.GetExecutingAssembly();object personInstance = assembly.CreateInstance("ReflectionDemo.Person");var person = (Person)personInstance;person.Show();}
}

运行结果:

无参构造函数被执行!
Person
▶ 带参构造
internal class Program
{static void Main(string[] args){Assembly assembly = Assembly.GetExecutingAssembly();// 传递参数调用构造函数object personInstance = assembly.CreateInstance("ReflectionDemo.Person",ignoreCase: false,bindingAttr: BindingFlags.Public | BindingFlags.Instance, // 指定公共实例构造函数binder: null,//默认null 即可args: new object[] { "Bob", 12 },culture: null,//默认null 即可activationAttributes: null//默认null 即可)!;var person = (Person)personInstance;person.Show();}
}

运行结果:

有参构造函数被执行,Name = Bob、Age = 12
Person
▶ 注意事项
  • args 数组的类型和顺序必须与目标构造函数的参数完全匹配。
  • Assembly.CreateInstance 相对低效,建议优先使用 Activator.CreateInstance 或工厂模式。
  • Assembly.CreateInstance 最终调用 Activator.CreateInstance,因此两者功能相似,但 Activator 更直接。

3)使用 Invoke执行构造函数 创建对象

▶ 无参构造
internal class Program
{static void Main(string[] args){Type personType = typeof(Person);// 配置构造函数参数列表var types = Type.EmptyTypes; //new Type[0];ConstructorInfo constructorInfo = personType.GetConstructor(types);// 使用Invoke 执行构造函数,并传入对应的参数 数组object personInstance = constructorInfo.Invoke(null);//(new object[0]);var person = (Person)personInstance;person.Show();}
}

运行结果:

无参构造函数被执行!
Person
▶ 带参构造
internal class Program
{static void Main(string[] args){Type personType = typeof(Person);// 配置构造函数参数列表var types = new Type[] { typeof(string), typeof(int) };ConstructorInfo constructorInfo = personType.GetConstructor(types);// 使用Invoke 执行构造函数,并传入对应的参数 数组object personInstance = constructorInfo.Invoke(new object[] { "Bob", 12 });var person = (Person)personInstance;person.Show();}
}

运行结果:

有参构造函数被执行,Name = Bob、Age = 12
Person
▶ 注意事项
  • 参数数组的类型和顺序必须与目标构造函数的参数完全匹配。
  • new Type[] { typeof(string), typeof(int) };new object[] { "Bob", 12 } 保持一致

3. 动态访问和操作成员

反射不仅限于获取类型和成员信息,还可以用于动态调用方法和访问字段或属性。

1)动态调用方法

使用Type对象的GetMethod方法获取MethodInfo对象,然后调用Invoke方法执行方法。

using System.Reflection;namespace ReflectionDemo
{class Calculator{public int Add(int a, int b) => a + b;public void Show() => Console.WriteLine("Calculator");}internal class Program{static void Main(string[] args){Type calculatorType = typeof(Calculator);object calculatorInstance = Activator.CreateInstance(calculatorType);// 获取指定名称的方法信息MethodInfo showMethod = calculatorType.GetMethod("Show");MethodInfo addMethod = calculatorType.GetMethod("Add");// 执行无参方法showMethod.Invoke(calculatorInstance, null);// 执行带参方法int result = (int)addMethod.Invoke(calculatorInstance, new object[] { 5, 3 });Console.WriteLine($"Result of Add(5, 3): {result}");}}
}

运行结果:

Calculator
Result of Add(5, 3): 8

2)访问字段和属性

▶ 访问字段

使用Type对象的GetField方法获取FieldInfo 对象,然后使用GetValueSetValue方法访问或设置属性值。

using System;
using System.Reflection;class Person
{public string Name { get; set; }private int age;
}class Program
{static void Main(){Type personType = typeof(Person);object personInstance = Activator.CreateInstance(personType);// 获取 FieldInfo 对象:私有字段FieldInfo ageField = personType.GetField("age", BindingFlags.NonPublic | BindingFlags.Instance);// 使用 SetValue 设置字段值ageField.SetValue(personInstance, 30);// 使用 GetValue 获取字段值int ageValue = (int)ageField.GetValue(personInstance);Console.WriteLine($"Person's Age: {ageValue}");}
}
▶ 访问属性

使用Type对象的GetProperty方法获取PropertyInfo对象,然后使用GetValueSetValue方法访问或设置属性值。

using System;
using System.Reflection;class Person
{public string Name { get; set; }
}class Program
{static void Main(){Type personType = typeof(Person);object personInstance = Activator.CreateInstance(personType);// 获取 PropertyInfo 对象PropertyInfo nameProperty = personType.GetProperty("Name");// 使用 SetValue 设置属性值nameProperty.SetValue(personInstance, "Bob");// 使用 GetValue 获取属性值string nameValue = (string)nameProperty.GetValue(personInstance);Console.WriteLine($"Person's Name: {nameValue}");}
}

三、高阶实战技巧

1. 动态加载程序集

1)获取程序集信息

反射可解析程序集的元数据,例如:

using System.Reflection;namespace ReflectionDemo
{internal class Program{static void Main(string[] args){// 获取当前程序集Assembly currentAssembly = Assembly.GetExecutingAssembly();// 获取当前程序集完全限定名称Console.WriteLine("程序集名称:" + currentAssembly.FullName);// 输出:程序集名称:ReflectionDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nullConsole.WriteLine("程序集名称:" + currentAssembly.GetName().FullName);// 输出:程序集名称:ReflectionDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nullConsole.WriteLine("程序集名称:" + currentAssembly.GetName().Name);// 输出:程序集名称:ReflectionDemoConsole.WriteLine("程序集版本:" + currentAssembly.GetName().Version);// 输出:程序集版本:1.0.0.0}}
}

2)获取类型信息

从程序集中获取特定的类型(类、结构等)信息。

// 获取当前程序集
Assembly currentAssembly = Assembly.GetExecutingAssembly();// 获取程序集中的所有类型
Type[] types = currentAssembly.GetTypes();// 获取指定名称的类型
Type myType = currentAssembly.GetType("Namespace.ClassName");

3)动态加载程序集

▶ 使用Assembly.Load 加载
// 通过程序集全名加载
string assemblyName = "MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
Assembly assembly = Assembly.Load(assemblyName);// 加载后自动加载依赖项(如 MyDependency.dll)
▶ 使用Assembly.LoadFrom 加载
// 通过路径加载,并自动加载依赖项
string path = @"C:\MyAssembly.dll";
Assembly assembly = Assembly.LoadFrom(path);// 如果 MyAssembly.dll 依赖 MyDependency.dll,会自动加载
▶ 使用Assembly.LoadFile 加载
// 通过路径加载,但不加载依赖项
string path = @"C:\MyAssembly.dll";
Assembly assembly = Assembly.LoadFile(path);// 手动加载依赖项(如 MyDependency.dll)
Assembly.LoadFile(@"C:\MyDependency.dll");

程序集加载的三种方式,可以在项目中添加该程序集的引用后使用,也可在未添加该程序集的情况下使用(某些情况下),这样就极大的丰富的项目的灵活性和扩展性

  • 有关程序集的强名称 或弱名称 相关信息,可见:C# Type类中Name、FullName、Namespace、AssemblyQualifiedName的区别 文中所涉及的程序集的强弱名称内容。
  • 有关这三种加载程序集方式的详细信息,可见:C# 动态加载程序集的三种方式

4)实现插件化架构的关键步骤

// 加载 DLL
Assembly pluginAssembly = Assembly.LoadFrom("Plugin.dll");// 获取类型
Type pluginType = pluginAssembly.GetType("Plugin.MainClass");// 创建实例
object plugin = Activator.CreateInstance(pluginType);// 调用插件方法
MethodInfo executeMethod = pluginType.GetMethod("Execute");
executeMethod.Invoke(plugin, null);

2. 访问私有成员

通过 BindingFlags 组合实现私有成员的访问:

// 访问私有字段
FieldInfo privateField = type.GetField("_privateField", BindingFlags.NonPublic | BindingFlags.Instance);
privateField.SetValue(instance, "secret");// 调用私有方法
MethodInfo privateMethod = type.GetMethod("InternalProcess",BindingFlags.NonPublic | BindingFlags.Instance);
privateMethod.Invoke(instance, null);

3. 泛型方法调用

使用MakeGenericMethod动态创建泛型方法实例

▶ 调用单类型参数的泛型方法
using System.Reflection;
using System.Xml.Linq;namespace ReflectionDemo
{public class GenericHelper{// 定义一个泛型方法,接受类型参数 T,并打印类型名称public static void PrintGenericType<T>(){Console.WriteLine($"泛型类型参数 T 的类型是:{typeof(T).Name}");}}internal class Program{static void Main(string[] args){// 1. 获取目标类的 Type 对象Type targetType = typeof(GenericHelper);// 2. 通过反射获取泛型方法的 MethodInfo(注意方法名和参数列表)// 这里方法 PrintGenericType<T> 没有参数,所以参数类型数组为空MethodInfo genericMethod = targetType.GetMethod("PrintGenericType");if (genericMethod == null){Console.WriteLine("未找到泛型方法!");return;}// 3. 使用 MakeGenericMethod 指定类型参数(例如 int 和 string)// 创建一个类型参数数组,这里指定 T 为 intType[] typeArgs = { typeof(int) };MethodInfo closedMethod = genericMethod.MakeGenericMethod(typeArgs);// 4. 调用闭合后的泛型方法closedMethod.Invoke(null, null); // 静态方法无需实例,参数数组为空// 再次调用,指定类型参数为 stringType[] typeArgs2 = { typeof(string) };MethodInfo closedMethod2 = genericMethod.MakeGenericMethod(typeArgs2);closedMethod2.Invoke(null, null);}}
}
▶ 调用多类型参数的泛型方法
public class GenericHelper
{public static void PrintTwoTypes<T1, T2>(){Console.WriteLine($"T1: {typeof(T1).Name}, T2: {typeof(T2).Name}");}
}// 通过反射调用:
MethodInfo genericMethod = targetType.GetMethod("PrintTwoTypes");
MethodInfo closedMethod = genericMethod.MakeGenericMethod(typeof(int), typeof(string));
closedMethod.Invoke(null, null); // 输出:T1: Int32, T2: String

4.代码示例

为了更直观地理解反射的使用,下面是一个完整的代码示例。

using System;
using System.Reflection;// 定义Person类
public class Person
{public string Name { get; set; }public int Age { get; set; }public Person(string name, int age){Name = name;Age = age;}public void Introduce(){Console.WriteLine($"My name is {Name}, and I am {Age} years old.");}public string GetGreeting(){return $"Hello, my name is {Name}.";}
}// 反射示例
public class ReflectionExample
{public static void Main(){// 获取Person类型的Type对象Type personType = typeof(Person);// 使用Activator.CreateInstance方法创建Person对象object person = Activator.CreateInstance(personType, "Alice", 30);Person alice = (Person)person;// 调用Introduce方法alice.Introduce();// 使用反射调用GetGreeting方法MethodInfo greetingMethod = personType.GetMethod("GetGreeting");string greeting = (string)greetingMethod.Invoke(alice, null);Console.WriteLine(greeting);// 使用反射访问和设置Name属性PropertyInfo nameProperty = personType.GetProperty("Name");string name = (string)nameProperty.GetValue(alice);Console.WriteLine($"Name: {name}");nameProperty.SetValue(alice, "Bob");name = (string)nameProperty.GetValue(alice);Console.WriteLine($"Updated Name: {name}");}
}

在这个示例中,我们通过反射获取了 Person 类的类型信息,动态创建了对象实例,并设置了字段和属性的值,最后调用了方法。

四、反射的实际应用场景

1. 应用场景

  • 插件系统开发:动态加载DLL实现功能扩展
  • ORM框架:实现对象关系映射
  • 序列化工具:动态解析对象结构
  • DI容器:依赖注入实现

2. 应用场景示例

  • 有关反射的 应用场景示例,可见:C# 反射的实际应用场景。

五、使用须知

1. 反射的优缺点

优点

  • 灵活性高:在运行时动态操作对象,适用于需要灵活处理的场景。
  • 功能强大:可以访问和操作程序的元数据,实现复杂的功能。适用于序列化场景。
  • 强扩展性:可以动态的加载外部的程序集,增强程序的扩展性,适用于插件系统场景。

缺点

  • 性能开销:反射操作通常比直接操作慢,因为它需要额外的查找和验证。
  • 安全性问题:反射可以访问私有成员,可能带来安全隐患。
  • 可读性差:代码可读性较差,维护难度增加。

2. 性能优化

1)性能优化策略

虽然反射提供了极大的灵活性,但其性能开销相对较高(反射涉及动态类型解析,比直接调用慢10-100倍。)。频繁使用反射可能会影响应用程序的性能,特别是在需要高效率的场景下。为了优化性能,可以考虑以下几点:

  • 缓存反射结果:如果多次调用相同的反射操作,可以将结果缓存起来,避免重复查找。
    • 对重复使用的Type对象进行缓存
    • 预获取MethodInfoPropertyInfo并缓存。
  • 使用表达式树:对于某些复杂的反射操作,可以使用表达式树来生成高效的IL代码。
    • 预编译表达式:使用System.Linq.Expressions生成动态方法
    • 使用 Delegate 或 Expression:将反射调用转为委托提高性能
  • 减少反射调用次数:尽量减少不必要的反射调用,尤其是在循环中。
  • BindingFlags优化:精确指定成员搜索范围

2)基准测试对比

操作类型直接调用反射调用委托缓存Emit IL
简单方法调用(100万次)5ms6500ms15ms8ms
属性访问(100万次)2ms3200ms10ms6ms

以上数据,仅供参考。

3)优化方案实现

▶ 方案1:缓存反射结果

缓存反射结果可以显著提高应用程序的性能,尤其是在频繁使用反射获取类型信息、方法调用等场景下。下面我将给出一个简单的C#示例,展示如何缓存反射的结果。

using System;
using System.Collections.Generic;
using System.Reflection;public class ReflectionCacheExample
{private static Dictionary<string, MethodInfo> _methodInfoCache = new Dictionary<string, MethodInfo>();public void ExecuteMethod(string methodName){var methodInfo = GetMethodInfo(methodName);if (methodInfo != null){// 调用方法methodInfo.Invoke(this, null);}else{Console.WriteLine($"未找到名为 {methodName} 的方法");}}private MethodInfo GetMethodInfo(string methodName){string key = $"{this.GetType().FullName}.{methodName}";// 检查缓存中是否已有该方法的信息if (!_methodInfoCache.TryGetValue(key, out MethodInfo cachedMethod)){// 如果缓存中没有,则通过反射查找并添加到缓存MethodInfo method = this.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);if (method != null){_methodInfoCache[key] = method;return method;}return null;}return cachedMethod;}// 示例方法private void PrintHello(){Console.WriteLine("Hello, World!");}
}class Program
{static void Main(string[] args){ReflectionCacheExample example = new ReflectionCacheExample();example.ExecuteMethod("PrintHello");  // 第一次调用,会进行反射查找example.ExecuteMethod("PrintHello");  // 第二次调用,直接从缓存读取}
}

代码说明

  • 在这个例子中,我们创建了一个ReflectionCacheExample类,它包含一个用于缓存方法信息的静态字典_methodInfoCache
  • GetMethodInfo方法首先尝试从缓存中检索方法信息。如果找不到,则通过反射获取方法信息,并将其存储在缓存中以便将来使用。
  • ExecuteMethod方法演示了如何利用缓存来执行方法。第一次调用时,由于缓存为空,所以需要通过反射查找方法;第二次调用时,直接从缓存中获取方法信息,提高了效率。

缓存属性信息

private static Dictionary<Type, PropertyInfo[]> _propertyCache = new();public static PropertyInfo[] GetCachedProperties(Type type)
{if (!_propertyCache.TryGetValue(type, out var props)){props = type.GetProperties();_propertyCache[type] = props;}return props;
}
▶ 方案2:委托缓存(推荐)

下面是一个完整的C#示例,展示了如何使用表达式树创建一个属性访问器(getter),并将其应用于具体的类实例中。我们将以Person类为例,并展示如何获取其Name属性的值。

using System;
using System.Linq.Expressions;
using System.Reflection;public class Person
{public string Name { get; set; }public int Age { get; set; }public Person(string name, int age){Name = name;Age = age;}
}public static class PropertyGetterFactory
{public static Func<object, object> CreatePropertyGetter(PropertyInfo prop){var objParam = Expression.Parameter(typeof(object), "obj");var castExpr = Expression.Convert(objParam, prop.DeclaringType);var propAccess = Expression.Property(castExpr, prop);var castResult = Expression.Convert(propAccess, typeof(object));return Expression.Lambda<Func<object, object>>(castResult, objParam).Compile();}
}class Program
{static void Main(string[] args){// 创建一个Person对象var personObj = new Person("Alice", 30);// 获取"Name"属性的PropertyInfovar propertyInfo = typeof(Person).GetProperty("Name");if (propertyInfo == null){Console.WriteLine("未找到指定的属性");return;}// 创建属性访问器var getter = PropertyGetterFactory.CreatePropertyGetter(propertyInfo);// 使用属性访问器获取属性值string name = (string)getter(personObj);// 输出结果Console.WriteLine($"Name: {name}");}
}

代码说明

  1. Person 类:

    • 包含两个属性:NameAge
    • 提供了一个构造函数用于初始化这两个属性。
  2. PropertyGetterFactory 类:

    • CreatePropertyGetter 方法接收一个 PropertyInfo 对象作为参数。
    • 使用表达式树构建一个从对象到其属性值的委托(Func<object, object>)。
    • 这个委托可以直接调用,传入目标对象,返回该对象对应属性的值。
  3. Main 方法:

    • 创建一个 Person 实例并初始化其属性。
    • 获取 Person 类的 Name 属性的 PropertyInfo
    • 调用 CreatePropertyGetter 方法生成属性访问器。
    • 使用生成的属性访问器获取 personObjName 属性值,并输出结果。

输出结果

Name: Alice

这种方式通过表达式树动态生成属性访问器,可以显著提高反射操作的性能,特别是在需要频繁访问同一属性的情况下。

// 表达式树优化
var param = Expression.Parameter(typeof(MyClass));
var propAccess = Expression.Property(param, "Name");
var lambda = Expression.Lambda<Func<MyClass, string>>(propAccess, param);
Func<MyClass, string> compiled = lambda.Compile();
string value = compiled(instance);
▶ 方案3:Emit动态生成

动态方法和IL生成是一种高级技术,通常用于性能优化或在运行时动态生成代码的场景。使用这些技术时需要小心,确保生成的IL代码是正确的并且符合预期的行为。

下面是一个完整的C#示例,展示了如何使用动态方法(DynamicMethod)和IL生成器(ILGenerator)来创建一个无参构造函数的委托(ObjectActivator)。我们将以一个简单的类Person为例,并展示如何使用这个委托实例化对象。

using System;
using System.Reflection;
using System.Reflection.Emit;public class Person
{public string Name { get; set; }public int Age { get; set; }public Person(){Name = "Unknown";Age = 0;}public override string ToString(){return $"Name: {Name}, Age: {Age}";}
}public delegate object ObjectActivator();public static class ObjectActivatorFactory
{public static ObjectActivator CreateParameterlessConstructor(Type type){// 确保类型有一个无参构造函数ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);if (constructor == null){throw new ArgumentException("类型必须包含一个无参构造函数。", nameof(type));}// 创建一个新的动态方法var dynamicMethod = new DynamicMethod(name: "CreateInstance",returnType: typeof(object),parameterTypes: null,owner: type);ILGenerator il = dynamicMethod.GetILGenerator();il.Emit(OpCodes.Newobj, constructor); // 调用无参构造函数il.Emit(OpCodes.Ret); // 返回新创建的对象return (ObjectActivator)dynamicMethod.CreateDelegate(typeof(ObjectActivator));}
}class Program
{static void Main(string[] args){try{// 创建一个用于实例化Person对象的委托ObjectActivator activator = ObjectActivatorFactory.CreateParameterlessConstructor(typeof(Person));// 使用委托创建Person对象object personObj = activator();// 输出结果Console.WriteLine(personObj.ToString());}catch (Exception ex){Console.WriteLine($"发生错误: {ex.Message}");}}
}

代码说明

  1. Person 类:

    • 包含两个属性:NameAge
    • 提供了一个无参构造函数,初始化Name为"Unknown",Age为0。
    • 重写了ToString方法,方便输出对象信息。
  2. ObjectActivatorFactory 类:

    • CreateParameterlessConstructor 方法接收一个 Type 对象作为参数。
    • 首先检查该类型是否包含无参构造函数,如果没有,则抛出异常。
    • 使用 DynamicMethod 创建一个新的动态方法,返回类型为 object,没有参数。
    • 使用 ILGenerator 生成中间语言(IL)代码,调用指定类型的无参构造函数并返回新创建的对象。
    • 最后,将动态方法编译为委托并返回。
  3. Main 方法:

    • 尝试创建一个用于实例化 Person 对象的委托。
    • 使用该委托创建一个 Person 对象。
    • 输出创建的对象信息。

输出结果

Name: Unknown, Age: 0
using System;
using System.Linq.Expressions;
using System.Reflection.Emit;public class Program
{public static void Main(){// 动态生成一个方法DynamicMethod dynamicMethod = new DynamicMethod("Add", typeof(int), new[] { typeof(int), typeof(int) }, typeof(Program).Module);ILGenerator il = dynamicMethod.GetILGenerator();il.Emit(OpCodes.Ldarg_0);il.Emit(OpCodes.Ldarg_1);il.Emit(OpCodes.Add);il.Emit(OpCodes.Ret);// 调用动态生成的方法Func<int, int, int> add = (Func<int, int, int>)dynamicMethod.CreateDelegate(typeof(Func<int, int, int>));Console.WriteLine(add(2, 3)); // 输出: 5}
}

4)反射替代方案

场景反射方案替代方案
高性能方法调用MethodInfo.Invoke表达式树编译委托
对象创建Activator.CreateInstance预编译工厂类
类型检查IsAssignableFrom泛型约束/模式匹配
元数据分析GetCustomAttributes源代码生成

3. 安全注意事项

  • 访问私有成员破坏封装性,可能导致代码脆弱。
  • 反射可绕过权限检查,需谨慎处理敏感操作。
  • 避免反射未信任的程序集,防止安全漏洞。

4. 反射开发原则

  • 最小化原则:只在必要时使用反射(仅在动态扩展、框架开发等必要场景使用反射。 )
  • 缓存原则:避免重复反射操作
  • 安全原则:严格校验输入参数
  • 性能原则:优先使用编译时方案
  • 封装原则:封装反射逻辑,将反射操作封装在工具类中,降低业务代码复杂度。

六、反射与dynamic 关键字

1. 替代方案

  • 对于简单场景,优先使用 dynamic 关键字:
    dynamic obj = new Person();
    obj.Name = "李四"; // 动态绑定
    

2. 反射与 dynamic 的区别

  • 反射:通过 Type 对象显式操作类型成员,灵活性高但性能低。
  • dynamic:编译时静态类型,运行时动态绑定,语法简洁但功能受限。

3. 反射与dynamic

internal class Program
{static void Main(string[] args){Type type = typeof(User);object o_user = Activator.CreateInstance(type);//o_user.Show() //不可能通过o_class1 调用Showdynamic d_user = Activator.CreateInstance(type);d_user.Show("sss");//可以通过d_user 调用方法Show//其实o_user 和 d_user得到结果都是一样的,// 但是因为 object 时编译时类型,object本身没有Show方法,因此调用会报错// 而dynamic 是运行时类型,编译状态下会绕过编译器的检查,直到真正运行后才确定其数据类型Console.ReadLine();}
}

结语

回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/72839.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

YOLO+OpenCV强强联手:高精度跌倒检测技术实战解析

目录 关于摔倒检测 摔倒检测核心逻辑 摔倒检测:联合多种逻辑判断 原理详细解释 1. 导入必要的库 2. 定义函数和关键点连接关系 3. 筛选有效关键点并计算边界框 4. 计算人体上下半身中心点和角度 5. 绘制关键点和连接线 6. 绘制角度标注和检测跌倒 7. 返回处理后的图…

AI入门7:python三种API方式调用本地Ollama+DeepSeek

回顾 书接上篇&#xff1a;各种方式搭建了本地知识库&#xff1a; AI入门&#xff1a;AI模型管家婆ollama的安装和使用-CSDN博客 AI入门2&#xff1a;本地AI部署&#xff0c;用ollama部署deepseek&#xff08;私有化部署&#xff09;-CSDN博客 AI入门3&#xff1a;给本地d…

内网安全-横向移动Kerberos 攻击SPN 扫描WinRMWinRSRDP

1.WinRM&WinRS 条件&#xff1a; 双方开启winrm winrs服务 2008版本以上默认开启&#xff0c;win 7默认关闭 检测使用cs内置端口扫描5985开放情况 进行连接 winrs -r:http://192.168.93.30:5985 -u:administrator -p:Whoami2021 whoami 2.内网-spn shell setspn -T …

YZi Labs 谈对 Plume 的投资:利用区块链创造现实价值的典范项目

3 月 17 日&#xff0c;YZi Labs 宣布投资 RWAfi 赛道项目 Plume&#xff0c;引发市场广泛关注。本轮融资是 Plume 在 去年 5 月和 12 月 连续两轮融资后的第三轮融资&#xff0c;代表着市场资本市场对于 Plume RWAfi 叙事以及其发展潜力的高度认可。 本次融资不仅提升了市场对…

互功率谱 cpsd

互功率谱(Cross-Power Spectral Density, CPSD)是信号处理中用于描述两个信号在频域中相关性的工具。它表示两个信号在不同频率下的功率分布及其相位关系,广泛应用于模态分析、系统辨识和信号匹配等领域。 matlab 实现 MATLAB 提供了 cpsd 函数来计算互功率谱。以下是使用 …

RocketMQ 架构

一、RocketMQ 核心架构概述 ​1. 主要组件 ​Name Server&#xff1a; 集群的「中枢神经」&#xff0c;负责 Topic 元数据管理&#xff08;如 Topic 分区分布、Broker 节点状态监控&#xff09;。 ​Broker&#xff1a; 消息存储与流转的核心节点&#xff0c;负责消息的持久化…

单片机学完开发板,如何继续提升自己的技能?

很多人学完开发板后都会卡在一个尴尬的阶段&#xff1a;觉得自己会的东西不少&#xff0c;但又不知道下一步该干啥。会点C语言&#xff0c;能烧录程序&#xff0c;能点亮LED&#xff0c;玩转按键&#xff0c;搞定串口等等&#xff0c;能用开发板做点小玩意儿&#xff0c;但面对…

olmOCR大模型:支持结构化精准提取复杂PDF文件内容

基于streamlit与olmOCR大模型实现的pdf提取工具 import os import json import subprocess import pandas as pd from pathlib import Path import shutil import time import re import streamlit as st# 创建工作目录 WORKSPACE_DIR "olmocr_workspace" os.maked…

五模型对比!Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量时间序列预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 光伏功率预测&#xff01;五模型对比&#xff01;Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量时间序列预测(Matlab2023b 多输入单输出) 1.程序已经调试好&#xff0c;替换数据集后&#xff0c;仅运…

SQLMesh 系列教程:Airbnb数据分析项目实战

在本文中&#xff0c;我们将探讨如何利用dbt项目的代码库来实现一个简单的SQLMesh项目。本文的基础是基于Udemy讲师为dbt课程创建的示例项目&#xff0c;可以在这个GitHub repo中获得。这个dbt项目是相对完整的示例&#xff0c;我们将使用它作为模板来演示SQLMesh&#xff08;下…

单片机写的小液晶屏驱动+汉字滚屏

单片机写的小液晶屏驱动汉字滚屏 stm32f401freertos内置HZK16 单片机汉字滚屏

【Golang那些事】go1.22和1.23 更新重点及测评

好久没有写文章了&#xff0c;攒了一年的Golang版本特性的技术点以及踩过的坑&#xff0c;那就在新年第一篇的文章中做一个总结吧&#xff1a; 一、关于迭代器 (一)迭代器去掉了共享共享内存 一个经典的面试题 说到Golang经典的面试题&#xff0c;大家可能都刷到过很多&…

【大模型实战篇】使用GPTQ量化QwQ-32B微调后的推理模型

1. 量化背景 之所以做量化&#xff0c;就是希望在现有的硬件条件下&#xff0c;提升性能。量化能将模型权重从高精度&#xff08;如FP32&#xff09;转换为低精度&#xff08;如INT8/FP16&#xff09;&#xff0c;内存占用可减少50%~75%。低精度运算&#xff08;如INT8&#xf…

【MySQL】架构

MySQL架构 和其它数据库相比&#xff0c;MySQL有点与众不同&#xff0c;它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎的架构上&#xff0c;插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离。这种架构可以根据业务的需求和实…

国内首台太空采矿机器人亮相,宇宙资源开发迈入新阶段

随着地球资源的日益枯竭&#xff0c;人类将目光投向了浩瀚的宇宙。太空采矿作为一项前沿科技&#xff0c;正逐步从科幻走向现实。近日&#xff0c;中国矿业大学成功研制出国内首台太空采矿机器人&#xff0c;标志着我国在太空资源开发领域迈出了重要一步。 太空采矿并非新鲜概念…

Kubeasz工具快速部署K8Sv1.27版本集群(二进制方式)

文章目录 一、基本信息二、服务器初始化操作三、使用Kubeasz部署K8S集群四、验证集群 一、基本信息 1、部署需要满足前提条件&#xff1a; 注意1&#xff1a;确保各节点时区设置一致、时间同步&#xff1b;注意2&#xff1a;确保在干净的系统上开始安装&#xff1b;注意3&…

Java 文件和IO流基础(生动形象版)

系列文章目录 Java文件和IO流基础部分 文件VSIO流 文章目录 系列文章目录前言一、文件的定义和理解&#xff1a; 1.专业定义&#xff1a; 2.文件系统和路径&#xff1a; 二、IO流的定义和分类 1.定义&#xff1a;2.流的分类&#xff1a;修饰器模式的核心作用&#xff1a;基础结…

Linux驱动学习笔记(四)

高级字符设备进阶 1.一个完整的IO过程包含以下几个步骤&#xff1a;1应用程序向操作系统发起IO调用请求(系统调用)&#xff1b;2操作系统准备数据&#xff0c;把IO设备的数据加载到内核缓冲区&#xff1b;3操作系统拷贝数据&#xff0c;把内核缓冲区的数据从内核空间拷贝到应用…

2025年,电脑还需要分区吗?

随着2025年的到来&#xff0c;电脑存储空间已经不像以前那么金贵&#xff0c;固态硬盘&#xff08;SSD&#xff09;容量更大、速度更快&#xff0c;云存储也成了日常标配。许多人开始质疑&#xff1a;电脑还需要像以前那样分区吗&#xff1f; 一、分区到底是什么意思&#xff…

Springboot项目集成maven-assembly-plugin进行打包

通常我们将应用部署到服务器的某个目录下&#xff0c;一般情况下我们会提供像target&#xff08;存放应用jar包&#xff09;&#xff0c;bin&#xff08;项目启动/停止脚本&#xff09;&#xff0c;config&#xff08;项目配置文件&#xff09;&#xff0c;logs&#xff08;项目…