C# 反射

概述

        System.Reflection 命名空间中的类与 System.Type 使你能够获取相关加载的程序集和其中定义的类型的信息,如类、接口和值类型等。可以使用反射在运行时创建、调用和访问类型实例。可以调用类型的方法或访问其字段和属性。

知识点

public class Cat
{
    //无参无返回值
    public void SayHello()
    {
        Console.WriteLine("Hello World!");
    }

    //重载方法一:有参有返回值
    public int Sum(int a,int b)
    {
        return a + b;   
    }

    //重载方法二
    public int Sum(int a, int b, int c)
    {
        return a + b + c;
    }

    //私有方法
    private void OnSerect()
    {
        Console.WriteLine("私有方法");
    }

    //静态方法
    public static void Speak()
    {
        Console.WriteLine("喵喵喵");
    }

    //泛型方法
    public void GenericsMethod<T>(T parm)
    {
        Console.WriteLine($"类型: {typeof(T)},值: {parm}");
    }

    // 包含 ref 参数的方法
    public void MethodWithRef(ref int value)
    {
        value *= 2;
    }

    // 包含 out 参数的方法
    public void MethodWithOut(out string result)
    {
        result = "Hello from out parameter";
    }

    // 同时包含 ref 和 out 参数的方法
    public bool TryParse(string input, ref int value, out string error)
    {
        if (int.TryParse(input, out value))
        {
            error = null;
            return true;
        }
        error = "Invalid number";
        return false;
    }
}

加载程序集

        程序集是.NET应用程序的基本构建块,是CLR(公共语言运行时)执行代码的基本单元。在C#中,程序集是编译后的代码和资源的集合,通常以.dll(类库)或.exe(可执行文件)形式在。

方法

描述

Load(String)

通过程序集名称加载Assembly对象

LoadFrom(String)

通过DLL文件名称加载Assembly对象

GetType(String)

通过Assembly获取程序集中类,参数必须是类的全名

GetTypes

通过Assembly获取程序集中所有的类

// 通过程序集名称加载Assembly对象
Assembly assembly = Assembly.Load("First");
// 通过DLL文件名称加载Assembly对象
Assembly assembly = Assembly.LoadFrom("First.dll");
// 通过Assembly获取程序集中的类
Type type = assembly.GetType("First.Cat");
// 通过Assembly获取程序集中所有的类
Type[] t = assembly.GetTypes();

根据类名获取类型并创建实例对象

//第一种方法 使用Type.GetType
Type type = Type.GetType("First.Cat");

//第二种方法 使用Assembly.GetType
Assembly assembly = Assembly.Load("First");
Type type = assembly.GetType("First.Cat");

var instance = Activator.CreateInstance(type) as Cat;
if (instance == null)
{
    Console.WriteLine("NO");
}
else
{
    Console.WriteLine("OK");
}

//初始化构造函数
//Cat instance = Activator.CreateInstance(type,666,"旺财") as Cat;

构造函数 

Type type = Type.GetType("First.Cat");//获取类类型
// 根据参数类型获取构造函数 
ConstructorInfo constructor = type.GetConstructor(new Type[] {typeof(int), typeof(string) });
// 构造Object数组,作为构造函数的输入参数 
object[] obj = new object[2] { 666,"旺财" };
// 调用构造函数生成对象 
object animal = constructor.Invoke(obj);
((Cat)animal).SayHello();

方法调用

公共方法

无参方法

Type type = Type.GetType("First.Cat");//获取类类型
Cat instance = Activator.CreateInstance(type) as Cat;//实例化类对象
if (instance != null)
{
    MethodInfo method = type.GetMethod("SayHello");//获取类中方法
    Console.WriteLine($"方法名:{method.Name}");
    Console.WriteLine($"返回值:{method.ReturnType}");
    method.Invoke(instance, null);
}

有参有返回值方法

Type type = Type.GetType("First.Cat");//获取类类型
Cat instance = Activator.CreateInstance(type) as Cat;//实例化类对象
if (instance != null)
{
    MethodInfo method = type.GetMethod("Sum");//获取类中方法
    ParameterInfo[] parameters = method.GetParameters();//获取参数列表
    foreach (var item in parameters)
    {
        Console.WriteLine($"参数名:{item.Name}");
        Console.WriteLine($"参数类型:{item.ParameterType}");
    }
    object sum = method.Invoke(instance, new object[] { 1, 2 });
    Console.WriteLine(sum);
}

私有方法

Type type = Type.GetType("First.Cat");//获取类类型
Cat instance = Activator.CreateInstance(type) as Cat;//实例化类对象
if (instance != null)
{
    MethodInfo method = type.GetMethod("OnSerect",BindingFlags.NonPublic|BindingFlags.Instance);
    method.Invoke(instance, null);
}

重载方法

Type type = Type.GetType("First.Cat");//获取类类型
Cat instance = Activator.CreateInstance(type) as Cat;//实例化类对象
if (instance != null)
{
    // 指定要调用的重载方法的参数类型
    Type[] paramTypes1 = { typeof(int), typeof(int) };
    Type[] paramTypes2 = { typeof(int), typeof(int), typeof(int) };

    MethodInfo method1 = type.GetMethod("Sum", paramTypes1);
    MethodInfo method2 = type.GetMethod("Sum", paramTypes2);

    // 调用重载方法
    var result1 = method1.Invoke(instance, new object[] { 1, 2 });
    var result2 = method2.Invoke(instance, new object[] { 1,2,3 });

    Console.WriteLine($"1 + 2 = {result1}");
    Console.WriteLine($"1 + 2 + 3 = {result2}");
}

静态方法

Type type = Type.GetType("First.Cat");//获取类类型
MethodInfo method = type.GetMethod("Speak");//获取静态方法
// 调用静态方法(第一个参数传null)
method.Invoke(null, null);

泛型方法


Type type = Type.GetType("First.Cat");//获取类类型
Cat instance = Activator.CreateInstance(type) as Cat;//实例化类对象

// 获取泛型方法定义
MethodInfo echoMethod = type.GetMethod("GenericsMethod");

// 指定具体类型参数
MethodInfo genericEcho = echoMethod.MakeGenericMethod(typeof(string));

// 调用方法
genericEcho.Invoke(instance, new object[] { "好好学习" });

包含ref/out参数的方法

Type type = Type.GetType("First.Cat");//获取类类型
Cat instance = Activator.CreateInstance(type) as Cat;//实例化类对象

//*************************调用包含ref参数方法*************************
MethodInfo methodWithRef = type.GetMethod("MethodWithRef");
// 准备参数 - ref参数需要是对象数组中的实际变量
int refValue = 5;
object[] refParams = new object[] { refValue };
// 调用方法
methodWithRef.Invoke(instance, refParams);
// 获取修改后的值
int modifiedValue = (int)refParams[0];
Console.WriteLine($"Ref value after call: {modifiedValue}"); // 输出: 10

//*************************调用包含out参数方法*************************
MethodInfo methodWithOut = type.GetMethod("MethodWithOut");
// 对于out参数,初始值不重要,但必须提供变量
object[] outParams = new object[] { null }; // out参数初始化为null
// 调用方法
methodWithOut.Invoke(instance, outParams);
// 获取输出值
string outResult = (string)outParams[0];
Console.WriteLine($"Out result: {outResult}"); // 输出: Hello from out parameter

//*************************调用包含ref和out参数方法*************************
MethodInfo tryParseMethod = type.GetMethod("TryParse");

// 准备参数
string input = "123";
int refValue1 = 0;  // ref参数初始值
string outError = null;  // out参数初始值
object[] parameters = new object[] { input, refValue1, outError };

// 调用方法
bool success = (bool)tryParseMethod.Invoke(instance, parameters);

// 获取结果
refValue1 = (int)parameters[1];  // ref参数在索引1位置
outError = (string)parameters[2];  // out参数在索引2位置

Console.WriteLine($"Success: {success}, Value: {refValue1}, Error: {outError ?? "null"}");
// 输出: Success: True, Value: 123, Error: null

BindingFlags 

         BindingFlags 是 System.Reflection 命名空间中的一个枚举类型,用于精确控制反射操作如何搜索和返回成员(方法、属性、字段等)。

注意事项

1、必须组合至少一个可见性标志(Public/NonPublic)和一个作用域标志(Instance/Static),否则可能返回空结果。

❌ 错误示例:GetMembers(BindingFlags.Instance)(缺少 PublicNonPublic

✅ 正确示例:GetMembers(BindingFlags.Public | BindingFlags.Instance)

2、默认行为:如果不传递 BindingFlagsGetMembers() 默认只返回公共实例成员(等效于 Public | Instance)。

枚举说明

标志

分类

说明

Public

成员可见性控制

包含公共(public)成员

NonPublic

包含非公共成员(privateprotectedinternalprotected internal

Instance

成员作用域控制

包含实例成员(非静态成员)

Static

包含静态成员(static

DeclaredOnly

成员搜索行为控制

仅返回当前类型直接定义的成员(不包括继承的成员)

FlattenHierarchy

返回继承链中所有层级的静态成员(仅对 Static 成员有效)

IgnoreCase

忽略成员名称的大小写(如 "name" 可以匹配 "Name"

ExactBinding

要求参数类型严格匹配(用于方法调用时)

GetField

其他

表示获取字段

SetField

表示设置字段

GetProperty

表示获取属性

SetProperty

表示设置属性

InvokeMethod

表示调用方法

CreateInstance

表示创建实例

Default

默认绑定行为

常见组合示例

(1) 获取所有公共实例成员

var flags = BindingFlags.Public | BindingFlags.Instance;
var members = typeof(MyClass).GetMembers(flags);

(2) 获取私有实例字段

var flags = BindingFlags.NonPublic | BindingFlags.Instance;
var fields = typeof(MyClass).GetFields(flags);

(3) 获取静态成员(包括继承的静态成员)

var flags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
var members = typeof(DerivedClass).GetMembers(flags);

(4) 获取当前类定义的所有成员(不包含继承的成员)

var flags = BindingFlags.Public | BindingFlags.NonPublic | 
            BindingFlags.Instance | BindingFlags.Static | 
            BindingFlags.DeclaredOnly;
var members = typeof(MyClass).GetMembers(flags);

(5) 调用私有方法

var flags = BindingFlags.NonPublic | BindingFlags.Instance;
var method = typeof(MyClass).GetMethod("PrivateMethod", flags);
method.Invoke(instance, null);

获取/修改字段和属性

using System;
using System.Reflection;

public class DemoClass
{
    public string PublicField = "Public Field";
    private int _privateField = 42;
    
    public string PublicProperty { get; set; } = "Public Property";
    private bool PrivateProperty { get; set; } = true;
    
    public static string StaticField = "Static Field";
    private static DateTime StaticProperty { get; set; } = DateTime.Now;
}

class Program
{
    static void Main()
    {
        DemoClass instance = new DemoClass();
        Type type = typeof(DemoClass);
        
        // 访问公共字段
        FieldInfo publicField = type.GetField("PublicField");
        Console.WriteLine($"PublicField: {publicField.GetValue(instance)}");
        
        // 修改私有字段
        FieldInfo privateField = type.GetField("_privateField", 
            BindingFlags.NonPublic | BindingFlags.Instance);
        privateField.SetValue(instance, 100);
        Console.WriteLine($"_privateField: {privateField.GetValue(instance)}");
        
        // 访问公共属性
        PropertyInfo publicProp = type.GetProperty("PublicProperty");
        publicProp.SetValue(instance, "Modified Value");
        Console.WriteLine($"PublicProperty: {publicProp.GetValue(instance)}");
        
        // 修改私有属性
        PropertyInfo privateProp = type.GetProperty("PrivateProperty", 
            BindingFlags.NonPublic | BindingFlags.Instance);
        privateProp.SetValue(instance, false);
        Console.WriteLine($"PrivateProperty: {privateProp.GetValue(instance)}");
        
        // 静态成员访问
        FieldInfo staticField = type.GetField("StaticField", 
            BindingFlags.Public | BindingFlags.Static);
        Console.WriteLine($"StaticField: {staticField.GetValue(null)}");
        
        PropertyInfo staticProp = type.GetProperty("StaticProperty", 
            BindingFlags.NonPublic | BindingFlags.Static);
        Console.WriteLine($"StaticProperty: {staticProp.GetValue(null)}");
    }
}

优缺点

        反射是C#中非常强大的功能,但也需要谨慎使用,因为它会带来性能开销和安全风险。合理使用反射可以极大地提高程序的灵活性和扩展性。

优点:

1、反射提高了程序的灵活性和扩展性。

2、降低耦合性,提高自适应能力。

3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

缺点:

1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。

2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

<think>好的,用户现在问的是关于C#反射的介绍。首先,我需要回忆一下反射的基本概念。反射C#中是一个强大的功能,允许程序在运行时获取类型信息并动态调用方法或访问属性。可能用户是刚开始学习C#,或者在做一些需要动态处理类型的项目,比如插件系统或者序列化工具。 接下来,用户的需求可能不仅仅是了解反射是什么,而是想知道它的实际应用场景、常见用法以及需要注意的问题。用户可能在使用反射时遇到了性能问题或者安全问题,所以需要强调这些方面。同时,用户可能希望看到具体的代码示例,这样能更好地理解如何操作。 然后,我需要考虑如何组织内容。首先定义反射,说明其作用。然后分点介绍常见用途,比如类型检查、动态创建对象、调用方法、访问属性和字段等。每个用途最好配上简单的代码示例,让用户一目了然。接着,要提到反射的高级应用,比如特性处理和动态加载程序集,这些都是实际开发中常用的场景。 然后,必须提到反射的优缺点。用户可能知道反射的强大,但不知道其性能开销和安全隐患,这部分需要详细说明,给出优化建议,比如缓存Type对象或使用dynamic关键字。此外,还要提到代码可读性和维护性问题,帮助用户全面了解反射的适用场景。 最后,总结部分要强调反射的灵活性和风险,给出最佳实践建议,比如在必要时使用,并考虑替代方案。这样用户不仅能了解反射,还能在实际项目中合理应用,避免滥用。 现在需要检查是否有遗漏的重要点,比如安全性中的部分信任环境,或者代码示例的正确性。确保示例代码简洁,没有错误。同时,保持语言口语化,避免过于技术化的术语,让初学者也能理解。另外,用户之前的提问是关于ERP多线程的问题,可能用户对性能方面比较敏感,所以反射的性能问题需要特别强调。</think>### C# 反射(Reflection)详解 反射(Reflection)是 C# 中一项强大的功能,允许程序在 **运行时** 动态获取类型信息、访问或操作对象成员(属性、方法、字段等),甚至动态创建类型实例。它是实现灵活性和动态行为的核心机制,但需谨慎使用以避免性能和安全问题。 --- #### **1. 反射的核心用途** - **运行时类型检查**:获取类型元数据(如类名、基类、接口、方法列表等)。 - **动态创建对象**:根据类型名称实例化对象。 - **调用未知方法**:动态执行类中未在编译期绑定的方法。 - **访问私有成员**:突破访问权限限制(需注意安全性)。 - **插件系统开发**:动态加载外部程序集(DLL)并扩展功能。 --- #### **2. 反射的基本操作** ##### **2.1 获取类型信息** 通过 `Type` 类表示类型的元数据,获取方式: ```csharp // 方式1:通过对象获取 Type type1 = obj.GetType(); // 方式2:通过类型名称获取 Type type2 = Type.GetType("System.String"); // 方式3:通过 typeof 运算符 Type type3 = typeof(string); ``` ##### **2.2 动态创建对象** ```csharp Type type = Type.GetType("MyNamespace.MyClass"); object instance = Activator.CreateInstance(type); // 调用无参构造函数 // 调用含参构造函数 object instanceWithArgs = Activator.CreateInstance(type, new object[] { "参数1", 100 }); ``` ##### **2.3 调用方法** ```csharp Type type = instance.GetType(); MethodInfo method = type.GetMethod("MethodName"); method.Invoke(instance, new object[] { 参数1, 参数2 }); // 调用实例方法 // 调用静态方法 MethodInfo staticMethod = type.GetMethod("StaticMethod"); staticMethod.Invoke(null, new object[] { 参数 }); ``` ##### **2.4 访问属性/字段** ```csharp // 获取或设置属性 PropertyInfo prop = type.GetProperty("PropertyName"); object value = prop.GetValue(instance); // 获取值 prop.SetValue(instance, "新值"); // 设置值 // 访问私有字段(需指定 BindingFlags) FieldInfo field = type.GetField("_privateField", BindingFlags.NonPublic | BindingFlags.Instance); object fieldValue = field.GetValue(instance); ``` --- #### **3. 反射的高级应用** ##### **3.1 特性(Attribute)处理** 通过反射读取类或成员上的自定义特性: ```csharp Type type = typeof(MyClass); object[] attrs = type.GetCustomAttributes(typeof(MyAttribute), false); if (attrs.Length > 0) { MyAttribute attr = (MyAttribute)attrs[0]; Console.WriteLine(attr.Description); } ``` ##### **3.2 动态加载程序集** ```csharp // 加载 DLL 文件 Assembly assembly = Assembly.LoadFrom("MyPlugin.dll"); // 遍历程序集中的所有类型 foreach (Type t in assembly.GetTypes()) { if (t.IsClass && !t.IsAbstract) { object plugin = Activator.CreateInstance(t); // 调用插件初始化方法 } } ``` --- #### **4. 反射的优缺点** | **优点** | **缺点** | |------------------------------|---------------------------------------| | 灵活性强,支持动态扩展 | 性能开销大(比直接调用慢 100~1000 倍) | | 实现插件化架构 | 代码可读性差,调试困难 | | 可绕过访问权限(慎用) | 可能引发安全问题(如访问私有成员) | --- #### **5. 性能优化建议** - **缓存 Type 对象**:避免重复调用 `GetType()` 或 `typeof()`。 - **使用 `dynamic` 关键字**:对已知类型动态调用时,`dynamic` 比反射更快。 - **预编译表达式树**:通过 `Expression` 类生成委托,替代反射调用。 ```csharp var method = typeof(MyClass).GetMethod("MyMethod"); var instance = Expression.Parameter(typeof(MyClass), "obj"); var call = Expression.Call(instance, method); var lambda = Expression.Lambda<Action<MyClass>>(call, instance).Compile(); lambda(myInstance); // 快速调用 ``` --- #### **6. 典型应用场景** - **依赖注入框架**:自动解析构造函数参数。 - **ORM 框架**:将数据库字段映射到实体属性。 - **序列化/反序列化**:动态处理 JSON/XML 数据。 - **单元测试工具**:动态执行测试方法。 --- ### **总结** 反射C# 中实现动态编程的利器,但需权衡其灵活性与性能损耗。**在以下场景优先考虑反射**: - 需要动态加载外部代码(如插件)。 - 无法在编译期确定类型(如配置文件驱动)。 - 开发通用框架或工具库。 **避免滥用**:在性能敏感场景(如循环内)或安全关键代码中谨慎使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值