C# 反射 Reflection

有一天我在想反射的时候,突然间就想到了这个东西 就如同照镜子一样。镜子里面反射出了正在照镜子的人。 反射跟这个的效果就是一样的。通过反射我们程序就可以知道自己长什么样子的。 通过使用反射API 我们可以用访问到 程序集 模块 类型 与及类型上面的一些元信息 Attribute, 这些统称为元数据(metadata)。

我们还可以使用反射 动态的创建对象,并对对象的属性字段进行取值和赋值,也可以调用里面的方法,包括私有方法。

反射优缺点

优点:

  1. 反射提高了程序的灵活性和扩展性。
  2. 降低耦合性,提高自适应能力。
  3. 它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

缺点:

  1. 性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
  2. 使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

访问特性 Attribute

下面的示例当中我们将演示如何通过Attribute来读取CSV文件。并且CSV文件的列名跟我们定义的属性名不一致的情况。

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;

namespace Malema.net
{
    //定义一个我们自己的 CSV 特性Attribute
    [AttributeUsage(AttributeTargets.Property)]
    public class CsvAttribute : Attribute
    {
        public CsvAttribute(string name)
        {
            this.Name = name;
        }

        public string Name { get; set; }
    }

    // 定义我们的股票数据类
    public class Bar
    {
        [Csv("openprice")]
        public float Open { get; set; }

        [Csv("closeprice")]
        public float Close { get; set; }
    }

    class Program
    {
        static async Task Main(string[] args)
        {
            //Csv的内容先处理成 下面的 dictionary形式,这边不展示出来了
            var lines = new List<Dictionary<string, string>>();
            lines.Add(new Dictionary<string, string> { ["openprice"] = "15", ["closeprice"] = "16" });
            lines.Add(new Dictionary<string, string> { ["openprice"] = "16", ["closeprice"] = "17" });
            var bars = GetRecords<Bar>(lines);
        }

        private static List<T> GetRecords<T>(List<Dictionary<string, string>> lines)
        {
            var list = new List<T>();
            var properites = typeof(T).GetProperties(); // 得到所有的attribute
            foreach (var line in lines)
            {
                var t = Activator.CreateInstance<T>(); // 创建一个实例
                foreach (var item in properites)
                {
                    var attribute = item.GetCustomAttribute<CsvAttribute>(); // 得到每一个属性的 attribute
                    var name = attribute.Name;

                    if (item.PropertyType == typeof(float)) //判断属性的类型是不是float
                    {
                        if (float.TryParse(line[name], out float value)) // 取出值。并转化成 float类型
                        {
                            item.SetValue(t, value);
                        }
                    }

                }
                list.Add(t);
            }
            return list;
        }
    }
}

bars的结果如下

这样做的缺点是性能不太好。性能比较好的方式。是用反射生成代码(Emit,或者构建一个类,然后编译它,加载进来)

找出实现某个接口的所有类

在多态里面我们提过可以通过反射来自动创建类型。 如下。

using System;
using System.Linq;
using System.Threading.Tasks;

namespace Malema.net
{
    public interface IPay
    {
        string GetPayUrl();
    }

    public class AliPay : IPay
    {
        public string GetPayUrl()
        {
            // some code
            return "AliPay Url";
        }
    }

    public class WeChatPay : IPay
    {
        public string GetPayUrl()
        {
            // some code
            return "WeChatPay Url";
        }
    }
    class Program
    {
        static async Task Main(string[] args)
        {
            IPay pay = CreatePayByUser();
            var url = pay.GetPayUrl();
            Console.WriteLine(url); //"WeChatPay Url"
        }

        public static IPay CreatePayByUser()
        {
            var payType = typeof(IPay);
            var types = payType.Assembly.GetTypes();
            var payTypes = types.Where(x => x.IsAssignableTo(payType) && !x.IsInterface && x.IsClass).ToList();
            var choosePayType = payTypes.FirstOrDefault(it => it.Name == "WeChatPay"); //"WeChatPay"这个是从参数当中获取的
            return Activator.CreateInstance(choosePayType) as IPay;
        }
    }
}

最近更新的
...