C# 反射

从元数据说起

元数据的定义是“描述数据的数据”(data about data)。以数据库为例,存储到数据库表中的数据是按照一定的组织结构,和一定的关系约束等等存放的,描述这种组织结构的信息,例如表结构,表约束,字段约束等等,就可以成为元数据。

C# 源代码由CLI生成中间语言,保留了描述类型信息,接口,继承关系,类成员的元数据。我们可以通过反射来获取这些信息,并且除了获取之外,还可以通过反射在运行时动态调用一个类型的成员(而不仅仅在编译时)

    • *

反射

System.Type

Type类是读取类型元数据信息的类,一个Type类的实例代表了目标类型的类型信息。

Type类表示类型声明,包括类类型、接口类型、数组类型、值类型、枚举类型、类型参数、泛型类型定义,以及开放或封闭构造的泛型类型。

Type 类定义在System命名空间下。

以下是Type类中的部分信息

属性说明
Assembly获得程序集
Attributes获得与类型关联的特性
BaseType获得基类型
FullName获得包括命名空间的类型名
Name获得当前成员的名称
Namespace获取Type的命名空间
Is—用于判断当前类型

注:
关于"Is—",包括:

  1. 判断是否是类: IsClass;
  2. 判断是否是枚举:IsEnum;
  3. 判断是否是接口:IsInterface;
  4. 判断是否是抽象的:IsAbstract;
  5. 判断是否是数组:IsArray;
  6. 判断可见性:IsPublic,IsSealled,IsVisible

‘’’'等等

方法说明
Assembly获得程序集
Attributes获得与类型关联的特性
BaseType获得基类型
FullName获得包括命名空间的类型名
Name获得当前成员的名称
Namespace获取Type的命名空间
Is—用于判断当前类型

如下代码,反射某个类型并输出其类型名称

            Type type = typeof(Type1);
            //获取名字
            Console.WriteLine(type.Name);
            //获取全称:包括命名空间
            Console.WriteLine(type.FullName);

更重要的是,通过这种方式可以动态引用类型成员,并进行赋值,读取,调用操作

            //获取方法
            Type1 typeinstance = new Type1();
            foreach (var item in type.GetMethods())
            {
                Console.WriteLine("方法:" + item.Name);
            }
            //为typeinstance赋值:
            type.GetProperty("StringProperty").SetValue(typeinstance, "hello 
reflect");
            Console.WriteLine("为typeinstance对象的属性StringProperty赋值:" + 
typeinstance.StringProperty);
            //调用方法
            string 
result=type.GetMethod("method").Invoke(typeinstance,null).ToString();
            Console.WriteLine("调用方法method得到的结果为:" + result);

例如:获取数据库某个表的数据时,通常会在代码中定义一个与该表对应的Model类。将读取到的datatable中的值一一赋给这个类是一件非常痛苦的事情(尤其是字段特别多的情况下)。
事实上,通常Model类并不会具有很多复杂的方法,而是定义仅仅定义一些字段。因此可以尝试将Model的属性字段和数据库中表的字段名称定义的完全相同,应用反射的方法进行赋值操作:

        public List<TESTTABLE> DateBaseHelper()

        {

            SqlConnection con = new SqlConnection("constr");
            string sql = "select *from TESTTABLE";
            SqlCommand cmd = new SqlCommand(sql,con);
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            DataSet ds = new DataSet();
            da.Fill(ds);
            List<TESTTABLE> tests = new List<TESTTABLE>();
            Type type = typeof(TESTTABLE);
            PropertyInfo[] properties = type.GetProperties();

            for(int i=0;i<ds.Tables[0].Rows.Count;i++)
            {
                TESTTABLE test = new TESTTABLE();
                foreach (var item in properties)
                {
                    item.SetValue(test, ds.Tables[0].Rows[i][item.Name]);
                }
                tests.Add(test);
            }
            return tests;
        }

注意:虽然这样做非常省代码,但是要考虑到反射也是有成本的(可能会影响性能,但是具体影响是否很大我没有做过深入了解)。这里主要用意还是想证明:反射是非常有用的。


typeof

typeof是一个运算符,对某一个类型名称或类型的实例使用相当于执行了GetType()方法。

<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、付费专栏及课程。

余额充值